opentui-markdown-render 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/src/app.tsx +11 -1
- package/src/index.ts +1 -0
- package/src/media.tsx +36 -21
- package/src/theme/index.ts +1 -1
- package/src/theme/syntax.ts +14 -3
- package/src/view/code.tsx +17 -3
- package/src/view/document.tsx +7 -4
- package/src/view/inline-flow.tsx +23 -13
- package/src/view/mermaid.tsx +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opentui-markdown-render",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "OpenTUI Markdown renderer with optional native LaTeX math",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -56,12 +56,12 @@
|
|
|
56
56
|
"string-width": "7.2.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@opentui/core": "0.
|
|
60
|
-
"@opentui/solid": "0.
|
|
59
|
+
"@opentui/core": "0.8.2",
|
|
60
|
+
"@opentui/solid": "0.8.2",
|
|
61
61
|
"@tsconfig/bun": "1.0.9",
|
|
62
62
|
"@types/bun": "1.4.0",
|
|
63
63
|
"@typescript/native-preview": "7.0.0-dev.20251207.1",
|
|
64
|
-
"solid-js": "1.9.
|
|
64
|
+
"solid-js": "1.9.18"
|
|
65
65
|
},
|
|
66
66
|
"engines": {
|
|
67
67
|
"bun": ">=1.4.0"
|
package/src/app.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CliRenderer } from "@opentui/core"
|
|
2
|
-
import { createMemo, createSignal } from "solid-js"
|
|
2
|
+
import { createEffect, createMemo, createSignal, onCleanup } from "solid-js"
|
|
3
3
|
import { useCanvasGraphics } from "./graphics"
|
|
4
4
|
import { resolveHeadingStyles } from "./headings"
|
|
5
5
|
import { rgbaToHex } from "./math"
|
|
@@ -9,6 +9,7 @@ import { parseDocument, type Document, type FrameConfig, type TablesConfig } fro
|
|
|
9
9
|
import { MarkdownFrame } from "./view/frame"
|
|
10
10
|
import { DocumentView, type RenderUnit } from "./view/document"
|
|
11
11
|
import type { Theme } from "./theme/types"
|
|
12
|
+
import { generateSyntax } from "./theme/syntax"
|
|
12
13
|
|
|
13
14
|
export type { ThemeModeSetting } from "./config"
|
|
14
15
|
export { loadRendererConfig } from "./config"
|
|
@@ -154,6 +155,14 @@ export function MarkdownRenderer(props: MarkdownRendererProps) {
|
|
|
154
155
|
// The look of the heading levels is a configuration of its own: the colours
|
|
155
156
|
// come from the theme, the attributes and the spacing from the config file.
|
|
156
157
|
const headings = createMemo(() => resolveHeadingStyles(config().headings))
|
|
158
|
+
const syntaxStyle = createMemo(() => {
|
|
159
|
+
const nativeScene = (renderer as CliRenderer & { readonly nativeScene?: object }).nativeScene
|
|
160
|
+
return generateSyntax(theme(), headings(), theme().markdown.codeBlock, nativeScene)
|
|
161
|
+
})
|
|
162
|
+
createEffect(() => {
|
|
163
|
+
const style = syntaxStyle()
|
|
164
|
+
onCleanup(() => void renderer.idle().then(() => style.destroy()))
|
|
165
|
+
})
|
|
157
166
|
return (
|
|
158
167
|
<box
|
|
159
168
|
width="100%"
|
|
@@ -178,6 +187,7 @@ export function MarkdownRenderer(props: MarkdownRendererProps) {
|
|
|
178
187
|
tableOptions={tableOptions()}
|
|
179
188
|
mermaidOptions={config().mermaid}
|
|
180
189
|
mathScale={mathScale()}
|
|
190
|
+
syntaxStyle={syntaxStyle()}
|
|
181
191
|
onDiagnostic={props.onDiagnostic}
|
|
182
192
|
renderUnit={props.renderUnit}
|
|
183
193
|
/>
|
package/src/index.ts
CHANGED
package/src/media.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NativeImage, imageInfo, parseColor, type ColorInput, type RGBA } from "@opentui/core"
|
|
2
|
-
import {
|
|
2
|
+
import { useRenderer } from "@opentui/solid"
|
|
3
|
+
import { decompressFrame, parseGIF, type ParsedGif } from "gifuct-js"
|
|
3
4
|
import path from "node:path"
|
|
4
5
|
import { createEffect, createResource, createSignal, Match, onCleanup, Show, Switch } from "solid-js"
|
|
5
6
|
|
|
@@ -44,8 +45,16 @@ export function rgbToHex(color: Rgb) {
|
|
|
44
45
|
return `#${channel(color[0])}${channel(color[1])}${channel(color[2])}`
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
type
|
|
48
|
-
type
|
|
48
|
+
type GifFrame = Parameters<typeof decompressFrame>[0]
|
|
49
|
+
type GifAnimation = {
|
|
50
|
+
readonly width: number
|
|
51
|
+
readonly height: number
|
|
52
|
+
readonly frames: readonly GifFrame[]
|
|
53
|
+
readonly colorTable: ParsedGif["gct"]
|
|
54
|
+
readonly target: { width: number; height: number }
|
|
55
|
+
readonly background: Rgb
|
|
56
|
+
}
|
|
57
|
+
type Media = { readonly box: { columns: number; rows: number }; readonly image?: NativeImage; readonly animation?: GifAnimation }
|
|
49
58
|
|
|
50
59
|
export { standaloneImage, type MarkdownImage } from "./core/image"
|
|
51
60
|
|
|
@@ -88,6 +97,7 @@ export function LocalImage(props: {
|
|
|
88
97
|
readonly textColor?: () => RGBA
|
|
89
98
|
readonly limits?: ImageLimits
|
|
90
99
|
}) {
|
|
100
|
+
const renderer = useRenderer()
|
|
91
101
|
const [failure, setFailure] = createSignal<string>()
|
|
92
102
|
// The cell size is part of the key so the image is reloaded and re-fitted once
|
|
93
103
|
// the terminal reports its pixel resolution (kitty queries it after startup).
|
|
@@ -145,26 +155,40 @@ export function LocalImage(props: {
|
|
|
145
155
|
|
|
146
156
|
createEffect(() => {
|
|
147
157
|
const value = media()
|
|
148
|
-
if (!value?.
|
|
158
|
+
if (!value?.animation) {
|
|
149
159
|
setFrame(value?.image)
|
|
150
160
|
return
|
|
151
161
|
}
|
|
162
|
+
const animation = value.animation
|
|
163
|
+
const canvas = new Uint8Array(animation.width * animation.height * 4)
|
|
152
164
|
let index = 0
|
|
165
|
+
let current: NativeImage | undefined
|
|
153
166
|
let timer: ReturnType<typeof setTimeout> | undefined
|
|
154
167
|
const next = () => {
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
168
|
+
const decoded = decompressFrame(animation.frames[index]!, animation.colorTable, true)
|
|
169
|
+
const before = decoded.disposalType === 3 ? canvas.slice() : undefined
|
|
170
|
+
drawPatch(canvas, animation.width, animation.height, decoded.patch, decoded.dims.left, decoded.dims.top, decoded.dims.width, decoded.dims.height)
|
|
171
|
+
const image = prepareImage(NativeImage.fromRgba(canvas.slice(), animation.width, animation.height), animation.target, animation.background)
|
|
172
|
+
const previous = current
|
|
173
|
+
current = image
|
|
174
|
+
setFrame(image)
|
|
175
|
+
if (previous) void renderer.idle().then(() => previous.dispose())
|
|
176
|
+
if (decoded.disposalType === 2) clearPatch(canvas, animation.width, decoded.dims.left, decoded.dims.top, decoded.dims.width, decoded.dims.height)
|
|
177
|
+
if (before) canvas.set(before)
|
|
178
|
+
index = (index + 1) % animation.frames.length
|
|
179
|
+
timer = setTimeout(next, Math.max(50, decoded.delay || 100))
|
|
159
180
|
}
|
|
160
181
|
next()
|
|
161
182
|
onCleanup(() => {
|
|
162
183
|
if (timer) clearTimeout(timer)
|
|
184
|
+
const image = current
|
|
185
|
+
if (image) void renderer.idle().then(() => image.dispose())
|
|
186
|
+
setFrame()
|
|
163
187
|
})
|
|
164
188
|
})
|
|
165
189
|
createEffect(() => {
|
|
166
190
|
const value = media()
|
|
167
|
-
onCleanup(() => disposeMedia(value))
|
|
191
|
+
onCleanup(() => void renderer.idle().then(() => disposeMedia(value)))
|
|
168
192
|
})
|
|
169
193
|
|
|
170
194
|
return (
|
|
@@ -197,21 +221,13 @@ export function LocalImage(props: {
|
|
|
197
221
|
)
|
|
198
222
|
}
|
|
199
223
|
|
|
200
|
-
function loadGif(bytes: Uint8Array, box: { columns: number; rows: number }, target: { width: number; height: number }, background: Rgb) {
|
|
224
|
+
export function loadGif(bytes: Uint8Array, box: { columns: number; rows: number }, target: { width: number; height: number }, background: Rgb) {
|
|
201
225
|
const gif = parseGIF(bytes.slice().buffer as ArrayBuffer)
|
|
202
226
|
const width = gif.lsd.width
|
|
203
227
|
const height = gif.lsd.height
|
|
204
|
-
const
|
|
205
|
-
const frames = decompressFrames(gif, true).map((frame) => {
|
|
206
|
-
const before = canvas.slice()
|
|
207
|
-
drawPatch(canvas, width, height, frame.patch, frame.dims.left, frame.dims.top, frame.dims.width, frame.dims.height)
|
|
208
|
-
const image = prepareImage(NativeImage.fromRgba(canvas.slice(), width, height), target, background)
|
|
209
|
-
if (frame.disposalType === 2) clearPatch(canvas, width, frame.dims.left, frame.dims.top, frame.dims.width, frame.dims.height)
|
|
210
|
-
if (frame.disposalType === 3) canvas.set(before)
|
|
211
|
-
return { image, delay: Math.max(50, frame.delay || 100) }
|
|
212
|
-
})
|
|
228
|
+
const frames = gif.frames.filter((frame): frame is GifFrame => "image" in frame)
|
|
213
229
|
if (!frames.length) throw new Error("GIF contains no frames")
|
|
214
|
-
return { box, frames }
|
|
230
|
+
return { box, animation: { width, height, frames, colorTable: gif.gct, target, background } }
|
|
215
231
|
}
|
|
216
232
|
|
|
217
233
|
function drawPatch(canvas: Uint8Array, width: number, height: number, patch: Uint8ClampedArray, left: number, top: number, patchWidth: number, patchHeight: number) {
|
|
@@ -263,5 +279,4 @@ function prepareImage(image: NativeImage, target: { width: number; height: numbe
|
|
|
263
279
|
|
|
264
280
|
function disposeMedia(media: Media | undefined) {
|
|
265
281
|
media?.image?.dispose()
|
|
266
|
-
media?.frames?.forEach((frame) => frame.image.dispose())
|
|
267
282
|
}
|
package/src/theme/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { defaultTheme } from "./defaults"
|
|
2
2
|
export { loadTheme, bundledThemeNames, themeFileNames } from "./load"
|
|
3
3
|
export type { ThemeEnvironment } from "./load"
|
|
4
|
-
export { generateSyntax } from "./syntax"
|
|
4
|
+
export { generateSyntax, type SyntaxResourceOwner } from "./syntax"
|
|
5
5
|
export { resolveTheme, mergeTheme, deriveStatusLineTheme } from "./resolve"
|
|
6
6
|
export { mixColors } from "./colors"
|
|
7
7
|
export type { Theme, ThemeMode, ThemePreset, ThemeOverride, StatusLineTheme, StatusLineThemeOverride, FrameTheme, FrameThemeOverride, MarkdownThemeOverride, HeadingLevel } from "./types"
|
package/src/theme/syntax.ts
CHANGED
|
@@ -2,7 +2,14 @@ import { SyntaxStyle, type RGBA, type ThemeTokenStyle } from "@opentui/core"
|
|
|
2
2
|
import { defaultHeadingStyles, headingColor, headingLevels, type HeadingStyles } from "../headings"
|
|
3
3
|
import type { HeadingLevel, Theme } from "./types"
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export type SyntaxResourceOwner = object
|
|
6
|
+
|
|
7
|
+
export function generateSyntax(
|
|
8
|
+
theme: Theme,
|
|
9
|
+
headings: HeadingStyles = defaultHeadingStyles,
|
|
10
|
+
defaultColor: RGBA = theme.text.base,
|
|
11
|
+
owner?: SyntaxResourceOwner,
|
|
12
|
+
) {
|
|
6
13
|
const { markdown, syntax, text } = theme
|
|
7
14
|
const feedback = text.feedback
|
|
8
15
|
|
|
@@ -22,7 +29,7 @@ export function generateSyntax(theme: Theme, headings: HeadingStyles = defaultHe
|
|
|
22
29
|
rule([`markup.heading.${level}`], headingColor(theme, level), headingStyle(level)),
|
|
23
30
|
)
|
|
24
31
|
|
|
25
|
-
|
|
32
|
+
const rules = [
|
|
26
33
|
rule(["default"], defaultColor),
|
|
27
34
|
rule(["prompt"], theme.accent),
|
|
28
35
|
rule(["comment", "comment.documentation"], syntax.comment, { italic: true }),
|
|
@@ -84,7 +91,11 @@ export function generateSyntax(theme: Theme, headings: HeadingStyles = defaultHe
|
|
|
84
91
|
rule(["error"], feedback.error, { bold: true }),
|
|
85
92
|
rule(["warning"], feedback.warning, { bold: true }),
|
|
86
93
|
rule(["info"], feedback.info),
|
|
87
|
-
]
|
|
94
|
+
]
|
|
95
|
+
// OpenTUI 0.8 makes native resource ownership explicit. Passing the owner is
|
|
96
|
+
// harmless on older supported releases, whose factory ignores extra args.
|
|
97
|
+
const fromTheme = SyntaxStyle.fromTheme as (rules: ThemeTokenStyle[], owner?: SyntaxResourceOwner) => SyntaxStyle
|
|
98
|
+
return fromTheme(rules, owner)
|
|
88
99
|
}
|
|
89
100
|
|
|
90
101
|
function rule(
|
package/src/view/code.tsx
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { infoStringToFiletype } from "@opentui/core"
|
|
1
|
+
import { infoStringToFiletype, type SyntaxStyle } from "@opentui/core"
|
|
2
|
+
import { useRenderer } from "@opentui/solid"
|
|
3
|
+
import { onCleanup } from "solid-js"
|
|
2
4
|
import { generateSyntax } from "../theme/syntax"
|
|
3
5
|
import type { Theme } from "../theme/types"
|
|
4
6
|
import type { HeadingStyles } from "../headings"
|
|
@@ -8,14 +10,26 @@ export type CodeBlockProps = {
|
|
|
8
10
|
language: string | null
|
|
9
11
|
theme: Theme
|
|
10
12
|
headings: HeadingStyles
|
|
13
|
+
syntaxStyle?: SyntaxStyle
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function resolveCodeFiletype(language: string | null) {
|
|
17
|
+
const token = language?.trim().split(/\s+/, 1)[0]?.toLowerCase() ?? ""
|
|
18
|
+
return token === "jsonc" ? "json" : infoStringToFiletype(language ?? "")
|
|
11
19
|
}
|
|
12
20
|
|
|
13
21
|
export function CodeBlock(props: CodeBlockProps) {
|
|
22
|
+
const renderer = useRenderer()
|
|
23
|
+
const nativeScene = (renderer as typeof renderer & { readonly nativeScene?: object }).nativeScene
|
|
24
|
+
const owned = props.syntaxStyle ? undefined : generateSyntax(props.theme, props.headings, props.theme.markdown.codeBlock, nativeScene)
|
|
25
|
+
onCleanup(() => {
|
|
26
|
+
if (owned) void renderer.idle().then(() => owned.destroy())
|
|
27
|
+
})
|
|
14
28
|
return (
|
|
15
29
|
<code
|
|
16
30
|
content={props.content}
|
|
17
|
-
filetype={
|
|
18
|
-
syntaxStyle={
|
|
31
|
+
filetype={resolveCodeFiletype(props.language)}
|
|
32
|
+
syntaxStyle={props.syntaxStyle ?? owned!}
|
|
19
33
|
fg={props.theme.markdown.codeBlock}
|
|
20
34
|
bg={props.theme.background.base}
|
|
21
35
|
conceal={false}
|
package/src/view/document.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createMemo, For, type Accessor, type JSX } from "solid-js"
|
|
2
2
|
import stringWidth from "string-width"
|
|
3
|
-
import { type TextChunk } from "@opentui/core"
|
|
3
|
+
import { type SyntaxStyle, type TextChunk } from "@opentui/core"
|
|
4
4
|
import { assertNever, type Block, type Document, type ListItem, type UnitInfo } from "../core/document"
|
|
5
5
|
import type { ResolvedTableOptions } from "../core/tables"
|
|
6
6
|
import type { ImagesConfig, MermaidConfig } from "../config/types"
|
|
@@ -31,6 +31,7 @@ export type DocumentViewProps = {
|
|
|
31
31
|
tableOptions: ResolvedTableOptions
|
|
32
32
|
mermaidOptions?: MermaidConfig
|
|
33
33
|
mathScale?: number
|
|
34
|
+
syntaxStyle?: SyntaxStyle
|
|
34
35
|
onDiagnostic?: (diagnostic: { level: "warning" | "error"; message: string }) => void
|
|
35
36
|
renderUnit?: RenderUnit
|
|
36
37
|
}
|
|
@@ -66,7 +67,7 @@ export function DocumentView(props: DocumentViewProps) {
|
|
|
66
67
|
if (input.block().type === "mermaid") {
|
|
67
68
|
return <MermaidDiagramView source={(input.block() as Extract<Block, { type: "mermaid" }>).content}
|
|
68
69
|
theme={props.theme} columns={input.columns} mermaidOptions={props.mermaidOptions}
|
|
69
|
-
headings={props.headings} onDiagnostic={props.onDiagnostic} />
|
|
70
|
+
headings={props.headings} syntaxStyle={props.syntaxStyle} onDiagnostic={props.onDiagnostic} />
|
|
70
71
|
}
|
|
71
72
|
const view = createMemo(() => {
|
|
72
73
|
const block = input.block()
|
|
@@ -97,9 +98,11 @@ export function DocumentView(props: DocumentViewProps) {
|
|
|
97
98
|
columns={columns > 2 ? columns - 2 : columns} quote />}</For>
|
|
98
99
|
</box>
|
|
99
100
|
case "list": return <ListBlockView block={() => block} columns={columns} quote={input.quote} wrapItems={false} />
|
|
100
|
-
case "code": return <CodeBlock content={block.content} language={block.language} theme={props.theme}
|
|
101
|
+
case "code": return <CodeBlock content={block.content} language={block.language} theme={props.theme}
|
|
102
|
+
headings={props.headings} syntaxStyle={props.syntaxStyle} />
|
|
101
103
|
case "mermaid": return <MermaidDiagramView source={block.content} theme={props.theme} columns={columns}
|
|
102
|
-
mermaidOptions={props.mermaidOptions} headings={props.headings}
|
|
104
|
+
mermaidOptions={props.mermaidOptions} headings={props.headings} syntaxStyle={props.syntaxStyle}
|
|
105
|
+
onDiagnostic={props.onDiagnostic} />
|
|
103
106
|
case "table": return <TableBlock block={block} options={props.tableOptions} theme={props.theme} headings={props.headings}
|
|
104
107
|
hyperlinks={props.graphics.hyperlinks?.() ?? true} />
|
|
105
108
|
case "literal": return <text fg={props.theme.markdown.text} wrapMode="word" content={block.text} />
|
package/src/view/inline-flow.tsx
CHANGED
|
@@ -25,24 +25,34 @@ export type InlineFlowProps = {
|
|
|
25
25
|
|
|
26
26
|
function PrefixSegment(props: { chunks: () => TextChunk[]; width: number }) {
|
|
27
27
|
let ref: TextRenderable | undefined
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
const update = () => {
|
|
29
|
+
const content = new StyledText(props.chunks())
|
|
30
|
+
if (ref) ref.content = content
|
|
31
|
+
return content
|
|
32
|
+
}
|
|
33
|
+
createEffect(update)
|
|
34
|
+
return <text ref={(renderable) => {
|
|
35
|
+
ref = renderable
|
|
36
|
+
renderable.content = update()
|
|
37
|
+
}} wrapMode="none" width={props.width} flexShrink={0} />
|
|
32
38
|
}
|
|
33
39
|
|
|
34
40
|
function StyledSegment(props: { entry: LayoutRun; textRow: number }) {
|
|
35
41
|
let ref: TextRenderable | undefined
|
|
36
|
-
|
|
42
|
+
const update = () => {
|
|
37
43
|
const atom = props.entry.atom
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
44
|
+
const content = atom.type === "text" ? new StyledText([{
|
|
45
|
+
__isChunk: true, text: atom.text, fg: atom.fg, attributes: atom.attributes,
|
|
46
|
+
...(atom.href ? { link: { url: atom.href } } : {}),
|
|
47
|
+
}]) : new StyledText([])
|
|
48
|
+
if (ref) ref.content = content
|
|
49
|
+
return content
|
|
50
|
+
}
|
|
51
|
+
createEffect(update)
|
|
52
|
+
return <text ref={(renderable) => {
|
|
53
|
+
ref = renderable
|
|
54
|
+
renderable.content = update()
|
|
55
|
+
}} width={props.entry.columns} flexShrink={0} wrapMode="none" marginTop={props.textRow} />
|
|
46
56
|
}
|
|
47
57
|
|
|
48
58
|
export function InlineFlow(props: InlineFlowProps) {
|
package/src/view/mermaid.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createEffect, createMemo, Show, type Accessor } from "solid-js"
|
|
2
|
-
import { type MouseEvent, type TextRenderable } from "@opentui/core"
|
|
2
|
+
import { type MouseEvent, type SyntaxStyle, type TextRenderable } from "@opentui/core"
|
|
3
3
|
import { measureMermaidDiagram, type PreparedDiagram } from "../merman/diagram"
|
|
4
4
|
import { detectMermaidDiagram } from "../merman/detect"
|
|
5
5
|
import { MermaidSyntaxError } from "../merman/diagnostics"
|
|
@@ -16,6 +16,7 @@ export type MermaidDiagramViewProps = {
|
|
|
16
16
|
columns: number
|
|
17
17
|
mermaidOptions?: MermaidConfig
|
|
18
18
|
headings?: HeadingStyles
|
|
19
|
+
syntaxStyle?: SyntaxStyle
|
|
19
20
|
onDiagnostic?: (diagnostic: { level: "error"; message: string }) => void
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -121,7 +122,7 @@ export function MermaidDiagramView(props: MermaidDiagramViewProps) {
|
|
|
121
122
|
})
|
|
122
123
|
return <box width="100%" flexDirection="column" flexShrink={0}>
|
|
123
124
|
<Show when={prepared()} fallback={<CodeBlock content={props.source} language="mermaid"
|
|
124
|
-
theme={props.theme} headings={props.headings ?? defaultHeadingStyles} />}>
|
|
125
|
+
theme={props.theme} headings={props.headings ?? defaultHeadingStyles} syntaxStyle={props.syntaxStyle} />}>
|
|
125
126
|
{(value) => <DiagramText prepared={value} />}
|
|
126
127
|
</Show>
|
|
127
128
|
</box>
|