@rokkit/ui 1.0.0-next.148 → 1.0.0-next.149
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 -2
- package/src/MarkdownRenderer.svelte +43 -0
- package/src/index.ts +3 -0
- package/src/markdown-plugin.ts +12 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rokkit/ui",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.149",
|
|
4
4
|
"description": "Data driven UI components for Rokkit applications",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -46,7 +46,9 @@
|
|
|
46
46
|
"@rokkit/core": "latest",
|
|
47
47
|
"@rokkit/data": "latest",
|
|
48
48
|
"@rokkit/states": "latest",
|
|
49
|
-
"@rokkit/actions": "latest"
|
|
49
|
+
"@rokkit/actions": "latest",
|
|
50
|
+
"marked": "^15.0.0",
|
|
51
|
+
"isomorphic-dompurify": "^2.0.0"
|
|
50
52
|
},
|
|
51
53
|
"peerDependencies": {
|
|
52
54
|
"shiki": "^3.23.0",
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { marked } from 'marked'
|
|
3
|
+
import type { Token } from 'marked'
|
|
4
|
+
import DOMPurify from 'isomorphic-dompurify'
|
|
5
|
+
import type { MarkdownPlugin } from './markdown-plugin.js'
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
markdown: string
|
|
9
|
+
plugins?: MarkdownPlugin[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let { markdown, plugins = [] }: Props = $props()
|
|
13
|
+
|
|
14
|
+
const pluginMap = $derived(
|
|
15
|
+
Object.fromEntries(plugins.map((p) => [p.language.toLowerCase(), p.component]))
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
const tokens = $derived(marked.lexer(markdown))
|
|
19
|
+
|
|
20
|
+
function tokenToSafeHtml(token: Token): string {
|
|
21
|
+
const tokenList = Object.assign([token], { links: (tokens as any).links ?? {} })
|
|
22
|
+
const raw = marked.parser(tokenList as any)
|
|
23
|
+
return DOMPurify.sanitize(raw)
|
|
24
|
+
}
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<div class="markdown-renderer" data-markdown>
|
|
28
|
+
{#each tokens as token, i (i)}
|
|
29
|
+
{#if token.type === 'code'}
|
|
30
|
+
{@const lang = (token.lang ?? '').toLowerCase()}
|
|
31
|
+
{@const Plugin = pluginMap[lang]}
|
|
32
|
+
{#if Plugin}
|
|
33
|
+
<Plugin code={token.text} />
|
|
34
|
+
{:else}
|
|
35
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
36
|
+
{@html tokenToSafeHtml(token)}
|
|
37
|
+
{/if}
|
|
38
|
+
{:else}
|
|
39
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
40
|
+
{@html tokenToSafeHtml(token)}
|
|
41
|
+
{/if}
|
|
42
|
+
{/each}
|
|
43
|
+
</div>
|
package/src/index.ts
CHANGED
|
@@ -43,6 +43,9 @@ export {
|
|
|
43
43
|
AlertList
|
|
44
44
|
} from './components/index.js'
|
|
45
45
|
|
|
46
|
+
export { default as MarkdownRenderer } from './MarkdownRenderer.svelte'
|
|
47
|
+
export type { MarkdownPlugin } from './markdown-plugin.js'
|
|
48
|
+
|
|
46
49
|
// Utilities
|
|
47
50
|
export { highlightCode, preloadHighlighter, getSupportedLanguages } from './utils/shiki.js'
|
|
48
51
|
export * from './utils/palette.js'
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Component } from 'svelte'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A plugin that renders a fenced code block as a Svelte component.
|
|
5
|
+
* The component receives { code: string } as props.
|
|
6
|
+
*/
|
|
7
|
+
export interface MarkdownPlugin {
|
|
8
|
+
/** Fenced code block language to match (e.g. 'plot', 'table', 'sparkline') */
|
|
9
|
+
language: string
|
|
10
|
+
/** Svelte component to render the block. Receives { code: string } */
|
|
11
|
+
component: Component<{ code: string }>
|
|
12
|
+
}
|