opentui-markdown-render 0.2.1 → 0.2.2
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 +12 -2
- package/src/index.ts +1 -0
- package/src/theme/index.ts +1 -1
- package/src/theme/syntax.ts +14 -3
- package/src/view/code.tsx +9 -2
- package/src/view/document.tsx +6 -3
- 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.2",
|
|
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
|
-
import type { CliRenderer } from "@opentui/core"
|
|
2
|
-
import { createMemo, createSignal } from "solid-js"
|
|
1
|
+
import type { CliRenderer, SyntaxStyle } from "@opentui/core"
|
|
2
|
+
import { 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
|
+
let currentSyntax: SyntaxStyle | undefined
|
|
159
|
+
const syntaxStyle = createMemo(() => {
|
|
160
|
+
currentSyntax?.destroy()
|
|
161
|
+
const nativeScene = (renderer as CliRenderer & { readonly nativeScene?: object }).nativeScene
|
|
162
|
+
currentSyntax = generateSyntax(theme(), headings(), theme().markdown.codeBlock, nativeScene)
|
|
163
|
+
return currentSyntax
|
|
164
|
+
})
|
|
165
|
+
onCleanup(() => currentSyntax?.destroy())
|
|
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/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,19 @@ export type CodeBlockProps = {
|
|
|
8
10
|
language: string | null
|
|
9
11
|
theme: Theme
|
|
10
12
|
headings: HeadingStyles
|
|
13
|
+
syntaxStyle?: SyntaxStyle
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
export function CodeBlock(props: CodeBlockProps) {
|
|
17
|
+
const renderer = useRenderer()
|
|
18
|
+
const nativeScene = (renderer as typeof renderer & { readonly nativeScene?: object }).nativeScene
|
|
19
|
+
const owned = props.syntaxStyle ? undefined : generateSyntax(props.theme, props.headings, props.theme.markdown.codeBlock, nativeScene)
|
|
20
|
+
onCleanup(() => owned?.destroy())
|
|
14
21
|
return (
|
|
15
22
|
<code
|
|
16
23
|
content={props.content}
|
|
17
24
|
filetype={infoStringToFiletype(props.language ?? "")}
|
|
18
|
-
syntaxStyle={
|
|
25
|
+
syntaxStyle={props.syntaxStyle ?? owned!}
|
|
19
26
|
fg={props.theme.markdown.codeBlock}
|
|
20
27
|
bg={props.theme.background.base}
|
|
21
28
|
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
|
}
|
|
@@ -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/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>
|