@humanspeak/svelte-markdown 1.0.0 → 1.0.1
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/README.md +109 -0
- package/dist/SvelteMarkdown.svelte +8 -5
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@ A powerful, customizable markdown renderer for Svelte with TypeScript support. B
|
|
|
20
20
|
- 🚀 Full markdown syntax support through Marked
|
|
21
21
|
- 💪 Complete TypeScript support with strict typing
|
|
22
22
|
- 🔄 Svelte 5 runes compatibility
|
|
23
|
+
- ✂️ Inline snippet overrides — customize renderers without separate files
|
|
23
24
|
- 🎨 Customizable component rendering system
|
|
24
25
|
- ♿ WCAG 2.1 accessibility compliance
|
|
25
26
|
- 🎯 GitHub-style slug generation for headers
|
|
@@ -259,6 +260,114 @@ Here's a complete example of a custom renderer with TypeScript support:
|
|
|
259
260
|
|
|
260
261
|
If you would like to extend other renderers please take a look inside the [renderers folder](https://github.com/humanspeak/svelte-markdown/tree/main/src/lib/renderers) for the default implentation of them. If you would like feature additions please feel free to open an issue!
|
|
261
262
|
|
|
263
|
+
## Snippet Overrides (Svelte 5)
|
|
264
|
+
|
|
265
|
+
For simple tweaks — adding a class, changing an attribute, wrapping in a div — you can override renderers inline with Svelte 5 snippets instead of creating separate component files:
|
|
266
|
+
|
|
267
|
+
```svelte
|
|
268
|
+
<script lang="ts">
|
|
269
|
+
import SvelteMarkdown from '@humanspeak/svelte-markdown'
|
|
270
|
+
|
|
271
|
+
const source = '# Hello\n\nA paragraph with [a link](https://example.com).'
|
|
272
|
+
</script>
|
|
273
|
+
|
|
274
|
+
<SvelteMarkdown {source}>
|
|
275
|
+
{#snippet paragraph({ children })}
|
|
276
|
+
<p class="prose">{@render children?.()}</p>
|
|
277
|
+
{/snippet}
|
|
278
|
+
|
|
279
|
+
{#snippet heading({ depth, children })}
|
|
280
|
+
{#if depth === 1}
|
|
281
|
+
<h1 class="title">{@render children?.()}</h1>
|
|
282
|
+
{:else}
|
|
283
|
+
<h2>{@render children?.()}</h2>
|
|
284
|
+
{/if}
|
|
285
|
+
{/snippet}
|
|
286
|
+
|
|
287
|
+
{#snippet link({ href, title, children })}
|
|
288
|
+
<a {href} {title} target="_blank" rel="noopener noreferrer">
|
|
289
|
+
{@render children?.()}
|
|
290
|
+
</a>
|
|
291
|
+
{/snippet}
|
|
292
|
+
|
|
293
|
+
{#snippet code({ lang, text })}
|
|
294
|
+
<pre class="highlight {lang}"><code>{text}</code></pre>
|
|
295
|
+
{/snippet}
|
|
296
|
+
</SvelteMarkdown>
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### How it works
|
|
300
|
+
|
|
301
|
+
- **Container renderers** (paragraph, heading, blockquote, list, etc.) receive a `children` snippet for nested content
|
|
302
|
+
- **Leaf renderers** (code, image, hr, br) receive only data props — no `children`
|
|
303
|
+
- **Precedence**: snippet > component renderer > default. If both a snippet and a `renderers.paragraph` component are provided, the snippet wins
|
|
304
|
+
|
|
305
|
+
### HTML tag snippets
|
|
306
|
+
|
|
307
|
+
HTML tag snippets use an `html_` prefix to avoid collisions with markdown renderer names:
|
|
308
|
+
|
|
309
|
+
```svelte
|
|
310
|
+
<SvelteMarkdown {source}>
|
|
311
|
+
{#snippet html_div({ attributes, children })}
|
|
312
|
+
<div class="custom-wrapper" {...attributes}>{@render children?.()}</div>
|
|
313
|
+
{/snippet}
|
|
314
|
+
|
|
315
|
+
{#snippet html_a({ attributes, children })}
|
|
316
|
+
<a {...attributes} target="_blank" rel="noopener noreferrer">
|
|
317
|
+
{@render children?.()}
|
|
318
|
+
</a>
|
|
319
|
+
{/snippet}
|
|
320
|
+
</SvelteMarkdown>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
All HTML snippets share a uniform props interface: `{ attributes?: Record<string, any>, children?: Snippet }`.
|
|
324
|
+
|
|
325
|
+
### Custom HTML Tags
|
|
326
|
+
|
|
327
|
+
You can render arbitrary (non-standard) HTML tags like `<click>`, `<tooltip>`, or any custom element by providing a renderer or snippet for the tag name. The parsing pipeline accepts any tag name — you just need to tell `SvelteMarkdown` how to render it.
|
|
328
|
+
|
|
329
|
+
**Component renderer approach:**
|
|
330
|
+
|
|
331
|
+
```svelte
|
|
332
|
+
<script lang="ts">
|
|
333
|
+
import SvelteMarkdown from '@humanspeak/svelte-markdown'
|
|
334
|
+
import ClickButton from './ClickButton.svelte'
|
|
335
|
+
|
|
336
|
+
const source = '<click>Click Me</click>'
|
|
337
|
+
const renderers = { html: { click: ClickButton } }
|
|
338
|
+
</script>
|
|
339
|
+
|
|
340
|
+
<SvelteMarkdown {source} {renderers} />
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**Snippet override approach:**
|
|
344
|
+
|
|
345
|
+
```svelte
|
|
346
|
+
<SvelteMarkdown source={'<click data-action="submit">Click Me</click>'}>
|
|
347
|
+
{#snippet html_click({ attributes, children })}
|
|
348
|
+
<button {...attributes} class="custom-btn">{@render children?.()}</button>
|
|
349
|
+
{/snippet}
|
|
350
|
+
</SvelteMarkdown>
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Both approaches work for any tag name. Snippet overrides take precedence over component renderers when both are provided.
|
|
354
|
+
|
|
355
|
+
### TypeScript
|
|
356
|
+
|
|
357
|
+
All snippet prop types are exported for use in external components:
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
import type {
|
|
361
|
+
ParagraphSnippetProps,
|
|
362
|
+
HeadingSnippetProps,
|
|
363
|
+
LinkSnippetProps,
|
|
364
|
+
CodeSnippetProps,
|
|
365
|
+
HtmlSnippetProps,
|
|
366
|
+
SnippetOverrides,
|
|
367
|
+
HtmlSnippetOverrides
|
|
368
|
+
} from '@humanspeak/svelte-markdown'
|
|
369
|
+
```
|
|
370
|
+
|
|
262
371
|
## Advanced Features
|
|
263
372
|
|
|
264
373
|
### Table Support with Mixed Content
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
type TokensList
|
|
60
60
|
} from './utils/markdown-parser.js'
|
|
61
61
|
import { parseAndCacheTokens } from './utils/parse-and-cache.js'
|
|
62
|
-
import { rendererKeysInternal
|
|
62
|
+
import { rendererKeysInternal } from './utils/rendererKeys.js'
|
|
63
63
|
|
|
64
64
|
const {
|
|
65
65
|
source = [],
|
|
@@ -118,15 +118,18 @@
|
|
|
118
118
|
// Collect HTML snippet overrides (keys matching html_<tag>)
|
|
119
119
|
const htmlSnippetOverrides = $derived(
|
|
120
120
|
Object.fromEntries(
|
|
121
|
-
|
|
122
|
-
.filter((key) =>
|
|
123
|
-
.map((key) => [key,
|
|
121
|
+
Object.entries(rest)
|
|
122
|
+
.filter(([key, val]) => key.startsWith('html_') && val != null)
|
|
123
|
+
.map(([key, val]) => [key.slice(5), val])
|
|
124
124
|
)
|
|
125
125
|
)
|
|
126
126
|
|
|
127
127
|
// Passthrough: everything that isn't a known snippet override
|
|
128
128
|
const snippetKeySet = $derived(
|
|
129
|
-
new Set([
|
|
129
|
+
new Set([
|
|
130
|
+
...rendererKeysInternal,
|
|
131
|
+
...Object.keys(rest).filter((k) => k.startsWith('html_'))
|
|
132
|
+
])
|
|
130
133
|
)
|
|
131
134
|
const passThroughProps = $derived(
|
|
132
135
|
Object.fromEntries(Object.entries(rest).filter(([key]) => !snippetKeySet.has(key)))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanspeak/svelte-markdown",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Fast, customizable markdown renderer for Svelte with built-in caching, TypeScript support, and Svelte 5 runes",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"svelte",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"@playwright/cli": "^0.1.1",
|
|
75
75
|
"@playwright/test": "^1.58.2",
|
|
76
76
|
"@sveltejs/adapter-auto": "^7.0.1",
|
|
77
|
-
"@sveltejs/kit": "^2.52.
|
|
77
|
+
"@sveltejs/kit": "^2.52.2",
|
|
78
78
|
"@sveltejs/package": "^2.5.7",
|
|
79
79
|
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
80
80
|
"@testing-library/jest-dom": "^6.9.1",
|
|
@@ -95,11 +95,11 @@
|
|
|
95
95
|
"jsdom": "^28.1.0",
|
|
96
96
|
"prettier": "^3.8.1",
|
|
97
97
|
"prettier-plugin-organize-imports": "^4.3.0",
|
|
98
|
-
"prettier-plugin-svelte": "^3.
|
|
98
|
+
"prettier-plugin-svelte": "^3.5.0",
|
|
99
99
|
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
100
100
|
"publint": "^0.3.17",
|
|
101
|
-
"svelte": "^5.
|
|
102
|
-
"svelte-check": "^4.4.
|
|
101
|
+
"svelte": "^5.53.0",
|
|
102
|
+
"svelte-check": "^4.4.1",
|
|
103
103
|
"typescript": "^5.9.3",
|
|
104
104
|
"typescript-eslint": "^8.56.0",
|
|
105
105
|
"vite": "^7.3.1",
|