svelte-streamdown 3.1.1 → 4.0.0
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 +116 -37
- package/dist/Elements/Citation.svelte +4 -0
- package/dist/Elements/Code.svelte +12 -50
- package/dist/Streamdown.svelte +12 -15
- package/dist/context.svelte.d.ts +9 -8
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/marked/index.d.ts +12 -0
- package/dist/marked/index.js +120 -35
- package/dist/marked/marked-br.js +6 -0
- package/dist/marked/marked-citations.js +6 -0
- package/dist/marked/marked-footnotes.js +12 -2
- package/dist/marked/marked-list.js +10 -1
- package/dist/marked/marked-math.js +5 -0
- package/dist/marked/marked-subsup.js +8 -0
- package/dist/marked/marked-table.js +15 -12
- package/dist/theme.d.ts +0 -3
- package/dist/theme.js +0 -2
- package/dist/utils/highlightThemes.d.ts +4 -0
- package/dist/utils/highlightThemes.js +14 -0
- package/dist/utils/hightlighter.svelte.d.ts +6 -29
- package/dist/utils/hightlighter.svelte.js +27 -212
- package/package.json +4 -6
- package/dist/utils/bundledLanguages.d.ts +0 -8
- package/dist/utils/bundledLanguages.js +0 -143
package/README.md
CHANGED
|
@@ -75,9 +75,9 @@ Full support for
|
|
|
75
75
|
|
|
76
76
|
### 💻 Interactive Code Blocks
|
|
77
77
|
|
|
78
|
-
- Syntax highlighting powered by
|
|
78
|
+
- Syntax highlighting powered by [@tanstack/highlight](https://github.com/TanStack/highlight) (synchronous, SSR-friendly, ~31KB min / ~11KB gzip for every language)
|
|
79
79
|
- Copy-to-clipboard functionality
|
|
80
|
-
- Support any
|
|
80
|
+
- Support any `@tanstack/highlight` theme, or your own
|
|
81
81
|
|
|
82
82
|
### 🔢 Mathematical Expressions
|
|
83
83
|
|
|
@@ -398,7 +398,7 @@ Here is how it works on each content update:
|
|
|
398
398
|
|
|
399
399
|
During streaming, newly received text almost always only changes the **last** block (and occasionally starts a new one). Every earlier block keeps an identical raw string, so Svelte skips its `lex()` call entirely — this is the equivalent of the memoized `Block` component in the React version. The block-splitting pass itself runs on every update, but it is the cheap pass; the costly inline parsing is what gets reused.
|
|
400
400
|
|
|
401
|
-
Code highlighting is incremental as well: a code block is only re-highlighted when its text changes
|
|
401
|
+
Code highlighting is incremental as well: a code block is only re-highlighted when its text changes. Highlighting itself is synchronous and runs during SSR, so there is no grammar to load, no loading state and no hydration flash.
|
|
402
402
|
|
|
403
403
|
> [!NOTE]
|
|
404
404
|
> There is intentionally no separate block-level parse cache (e.g. an LRU keyed by block content). For the common append-only streaming case the reactivity-based approach above already avoids redundant work, and a standalone cache would add memory usage and invalidation complexity without a measurable benefit. If you have a workload where this matters, please [open an issue](https://github.com/beynar/svelte-streamdown/issues) with a repro — we're happy to revisit.
|
|
@@ -524,7 +524,7 @@ Streamdown is optimized for minimal bundle size by making heavy components **opt
|
|
|
524
524
|
<script>
|
|
525
525
|
import { Streamdown } from 'svelte-streamdown';
|
|
526
526
|
// Import only the components you need
|
|
527
|
-
import Code from 'svelte-streamdown/code'; //
|
|
527
|
+
import Code from 'svelte-streamdown/code'; // syntax highlighting
|
|
528
528
|
import Mermaid from 'svelte-streamdown/mermaid'; // Mermaid diagrams
|
|
529
529
|
import Math from 'svelte-streamdown/math'; // KaTeX math rendering
|
|
530
530
|
</script>
|
|
@@ -534,11 +534,11 @@ Streamdown is optimized for minimal bundle size by making heavy components **opt
|
|
|
534
534
|
|
|
535
535
|
### Component Dependencies
|
|
536
536
|
|
|
537
|
-
| Component | Import Path | Dependency
|
|
538
|
-
| --------- | --------------------------- |
|
|
539
|
-
| `Code` | `svelte-streamdown/code` |
|
|
540
|
-
| `Mermaid` | `svelte-streamdown/mermaid` | Mermaid.js
|
|
541
|
-
| `Math` | `svelte-streamdown/math` | KaTeX
|
|
537
|
+
| Component | Import Path | Dependency | Size Impact |
|
|
538
|
+
| --------- | --------------------------- | --------------------- | ---------------------------------------------------- |
|
|
539
|
+
| `Code` | `svelte-streamdown/code` | `@tanstack/highlight` | ~31KB min / ~11KB gzip (all 30 languages + 2 themes) |
|
|
540
|
+
| `Mermaid` | `svelte-streamdown/mermaid` | Mermaid.js | ~1.5MB |
|
|
541
|
+
| `Math` | `svelte-streamdown/math` | KaTeX | ~300KB |
|
|
542
542
|
|
|
543
543
|
> [!TIP]
|
|
544
544
|
> Only import the components your application actually uses. If your content doesn't include code blocks, mermaid diagrams, or math expressions, you can skip those imports entirely for a much smaller bundle.
|
|
@@ -551,71 +551,150 @@ When a heavy component is not provided:
|
|
|
551
551
|
- **Mermaid**: Renders the mermaid source as a code block
|
|
552
552
|
- **Math**: Renders the raw LaTeX/math text
|
|
553
553
|
|
|
554
|
-
###
|
|
554
|
+
### Highlight themes
|
|
555
555
|
|
|
556
|
-
The `Code` component bundles two themes out of the box: **`github-dark`** and **`github-light`**. By default `
|
|
556
|
+
The `Code` component bundles two themes out of the box: **`github-dark`** and **`github-light`**. By default `highlightTheme` follows the active color scheme (`github-dark` in dark mode, `github-light` otherwise), so basic light/dark theming works with no extra configuration.
|
|
557
557
|
|
|
558
|
-
To use any other
|
|
558
|
+
To use any other theme (`aurora-x`, `dracula`, `gruvbox-dark`, `gruvbox-light`, `monokai`, `nord`, `one-dark-pro`, `solarized-dark`, `solarized-light`), import it from `@tanstack/highlight/themes/<name>` and register it via the `highlightThemes` prop. The **key** you register it under is the value you pass to `highlightTheme`:
|
|
559
559
|
|
|
560
560
|
```svelte
|
|
561
561
|
<script lang="ts">
|
|
562
562
|
import { Streamdown } from 'svelte-streamdown';
|
|
563
|
-
import Code from 'svelte-streamdown/code'; // enables
|
|
564
|
-
import
|
|
563
|
+
import Code from 'svelte-streamdown/code'; // enables highlighting
|
|
564
|
+
import dracula from '@tanstack/highlight/themes/dracula';
|
|
565
565
|
|
|
566
566
|
let { content } = $props();
|
|
567
567
|
</script>
|
|
568
568
|
|
|
569
|
-
<Streamdown
|
|
569
|
+
<Streamdown
|
|
570
|
+
{content}
|
|
571
|
+
components={{ code: Code }}
|
|
572
|
+
highlightThemes={{ dracula }}
|
|
573
|
+
highlightTheme="dracula"
|
|
574
|
+
/>
|
|
570
575
|
```
|
|
571
576
|
|
|
572
|
-
> [!
|
|
573
|
-
>
|
|
577
|
+
> [!NOTE]
|
|
578
|
+
> An unknown `highlightTheme` key falls back to the built-in `github-dark` / `github-light` (following the color scheme) instead of leaving the code block unhighlighted.
|
|
579
|
+
|
|
580
|
+
A custom theme is just an object of the `HighlightTheme` shape (exported from `svelte-streamdown`) — a `name`, `type`, `background`, `foreground` and a color per token class:
|
|
581
|
+
|
|
582
|
+
```svelte
|
|
583
|
+
<script lang="ts">
|
|
584
|
+
import { Streamdown, type HighlightTheme } from 'svelte-streamdown';
|
|
585
|
+
import Code from 'svelte-streamdown/code';
|
|
586
|
+
import githubDark from '@tanstack/highlight/themes/github-dark';
|
|
587
|
+
|
|
588
|
+
const myTheme: HighlightTheme = {
|
|
589
|
+
...githubDark,
|
|
590
|
+
name: 'my-theme',
|
|
591
|
+
tokens: { ...githubDark.tokens, keyword: '#ff0088', comment: '#5a5a5a' }
|
|
592
|
+
};
|
|
593
|
+
</script>
|
|
594
|
+
|
|
595
|
+
<Streamdown
|
|
596
|
+
{content}
|
|
597
|
+
components={{ code: Code }}
|
|
598
|
+
highlightThemes={{ 'my-theme': myTheme }}
|
|
599
|
+
highlightTheme="my-theme"
|
|
600
|
+
/>
|
|
601
|
+
```
|
|
574
602
|
|
|
575
603
|
#### Dynamic (light/dark) theme switching
|
|
576
604
|
|
|
577
|
-
Register every theme you intend to switch between in `
|
|
605
|
+
Register every theme you intend to switch between in `highlightThemes`, then drive `highlightTheme` from your color-scheme store. Switching is fully reactive:
|
|
578
606
|
|
|
579
607
|
```svelte
|
|
580
608
|
<script lang="ts">
|
|
581
609
|
import { Streamdown } from 'svelte-streamdown';
|
|
582
610
|
import Code from 'svelte-streamdown/code';
|
|
583
611
|
import { mode } from 'mode-watcher';
|
|
584
|
-
import
|
|
585
|
-
import
|
|
612
|
+
import oneDarkPro from '@tanstack/highlight/themes/one-dark-pro';
|
|
613
|
+
import solarizedLight from '@tanstack/highlight/themes/solarized-light';
|
|
586
614
|
|
|
587
615
|
let { content } = $props();
|
|
588
616
|
|
|
589
|
-
const
|
|
590
|
-
mode.current === 'dark' ? 'github-dark-default' : 'github-light-default'
|
|
591
|
-
);
|
|
617
|
+
const highlightTheme = $derived(mode.current === 'dark' ? 'one-dark-pro' : 'solarized-light');
|
|
592
618
|
</script>
|
|
593
619
|
|
|
594
620
|
<Streamdown
|
|
595
621
|
{content}
|
|
596
622
|
components={{ code: Code }}
|
|
597
623
|
baseTheme="shadcn"
|
|
598
|
-
|
|
599
|
-
'
|
|
600
|
-
'
|
|
624
|
+
highlightThemes={{
|
|
625
|
+
'one-dark-pro': oneDarkPro,
|
|
626
|
+
'solarized-light': solarizedLight
|
|
601
627
|
}}
|
|
602
|
-
{
|
|
628
|
+
{highlightTheme}
|
|
603
629
|
/>
|
|
604
630
|
```
|
|
605
631
|
|
|
606
632
|
> [!NOTE]
|
|
607
|
-
> The built-in `github-dark` / `github-light` themes can be switched dynamically with just `
|
|
633
|
+
> The built-in `github-dark` / `github-light` themes can be switched dynamically with just `highlightTheme` (no `highlightThemes` registration needed).
|
|
634
|
+
|
|
635
|
+
#### Styling tokens with CSS
|
|
636
|
+
|
|
637
|
+
Every token span carries `th-token` plus a `th-<class>` class (`th-keyword`, `th-string`, `th-comment`, `th-function`, `th-number`, `th-operator`, `th-tag`, `th-attr`, …) alongside its inline color from the active theme, so you can tweak or override token styles from CSS:
|
|
638
|
+
|
|
639
|
+
```css
|
|
640
|
+
[data-streamdown-code] .th-comment {
|
|
641
|
+
font-style: italic;
|
|
642
|
+
}
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### Supported languages
|
|
646
|
+
|
|
647
|
+
All 30 scanners ship in the same ~31KB bundle, so there is nothing to lazy-load:
|
|
648
|
+
|
|
649
|
+
`apache`, `cmake`, `cpp`, `css`, `diff`, `dockerfile`, `ejs`, `env`, `go`, `html`, `http`, `js`, `json`, `jsx`, `markdown`, `mermaid`, `nginx`, `php`, `plaintext`, `python`, `scheme`, `shell`, `sql`, `svelte`, `toml`, `ts`, `tsrx`, `tsx`, `vue`, `yaml`.
|
|
650
|
+
|
|
651
|
+
Common aliases are normalized (`javascript` → `js`, `typescript` → `ts`, `bash`/`sh`/`zsh` → `shell`, `py` → `python`, `jsonc`/`json5` → `json`, `xml`/`htm` → `html`, `md` → `markdown`, `yml` → `yaml`, `docker` → `dockerfile`).
|
|
652
|
+
|
|
653
|
+
> [!IMPORTANT]
|
|
654
|
+
> Any other language — including `java`, `rust`, `c`, `c#`, `swift`, `kotlin`, `ruby` and `graphql` — is **not** highlighted as of `@tanstack/highlight@0.1.0`: the code block still renders (and keeps its copy/download UI), but as plaintext.
|
|
655
|
+
|
|
656
|
+
You can add your own language with `defineLanguage` and the `highlightLanguages` prop. A language is a tokenizer returning `{ className, start, end }` ranges:
|
|
657
|
+
|
|
658
|
+
```svelte
|
|
659
|
+
<script lang="ts">
|
|
660
|
+
import { Streamdown, defineLanguage } from 'svelte-streamdown';
|
|
661
|
+
import Code from 'svelte-streamdown/code';
|
|
662
|
+
|
|
663
|
+
const brainfuck = defineLanguage({
|
|
664
|
+
name: 'brainfuck',
|
|
665
|
+
tokenize: (code) =>
|
|
666
|
+
[...code.matchAll(/[+\-<>]+/g)].map((match) => ({
|
|
667
|
+
className: 'operator' as const,
|
|
668
|
+
start: match.index,
|
|
669
|
+
end: match.index + match[0].length
|
|
670
|
+
}))
|
|
671
|
+
});
|
|
672
|
+
</script>
|
|
673
|
+
|
|
674
|
+
<Streamdown {content} components={{ code: Code }} highlightLanguages={[brainfuck]} />
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
> [!NOTE]
|
|
678
|
+
> Keep the array passed to `highlightLanguages` referentially stable (define it outside the template): a new array builds a new highlighter.
|
|
608
679
|
|
|
609
|
-
|
|
680
|
+
### Migrating from v3 (shiki)
|
|
610
681
|
|
|
611
|
-
|
|
682
|
+
v4 replaces shiki with `@tanstack/highlight`. Highlighting is now synchronous, renders during SSR and no longer has a skeleton/loading state. Remove `shiki` / `@shikijs/*` from your dependencies and rename the props:
|
|
612
683
|
|
|
613
684
|
```diff
|
|
614
|
-
-
|
|
615
|
-
|
|
685
|
+
- shikiTheme="nord"
|
|
686
|
+
- shikiThemes={{ nord }}
|
|
687
|
+
- shikiLanguages={[{ id: 'haskell', name: 'Haskell', import: () => import('@shikijs/langs/haskell') }]}
|
|
688
|
+
+ highlightTheme="nord"
|
|
689
|
+
+ highlightThemes={{ nord }}
|
|
690
|
+
+ highlightLanguages={[myLanguage]}
|
|
616
691
|
```
|
|
617
692
|
|
|
618
|
-
|
|
693
|
+
- Theme objects come from `@tanstack/highlight/themes/<name>` instead of `@shikijs/themes/<name>`, and follow the `HighlightTheme` shape (not shiki's `ThemeRegistration`).
|
|
694
|
+
- `highlightLanguages` takes `LanguageDefinition`s built with `defineLanguage`, not lazy shiki grammar loaders.
|
|
695
|
+
- `bundledLanguagesInfo`, `createLanguageSet` and `LanguageInfo` are gone; `defineLanguage`, `LanguageDefinition` and `HighlightTheme` are exported instead.
|
|
696
|
+
- The `code.skeleton` theme key is gone (there is no loading state anymore).
|
|
697
|
+
- Languages shiki covered but `@tanstack/highlight@0.1.0` does not (`java`, `rust`, `c`, `c#`, `swift`, `kotlin`, `ruby`, `graphql`, …) now render as plaintext.
|
|
619
698
|
|
|
620
699
|
## 📋 Props API
|
|
621
700
|
|
|
@@ -634,9 +713,9 @@ This also keeps the default bundle small: only the themes you actually import ar
|
|
|
634
713
|
| `theme` | `DeepPartial<Theme>` | - | Custom theme overrides |
|
|
635
714
|
| `baseTheme` | `'tailwind' \| 'shadcn'` | `'tailwind'` | Base theme to use before applying overrides |
|
|
636
715
|
| `mergeTheme` | `boolean` | `true` | Whether to merge theme with base theme |
|
|
637
|
-
| `
|
|
638
|
-
| `
|
|
639
|
-
| `
|
|
716
|
+
| `highlightTheme` | `string` | auto (dark-mode aware) | Code highlighting theme. Defaults to `github-dark` in dark mode / `github-light` otherwise. Any other value must be a key registered via `highlightThemes`. See [Highlight themes](#highlight-themes). |
|
|
717
|
+
| `highlightThemes` | `Record<string, HighlightTheme>` | - | Register additional pre-imported themes (e.g. `{ dracula }`) so they can be selected via `highlightTheme`, including dynamic light/dark switching. |
|
|
718
|
+
| `highlightLanguages` | `LanguageDefinition[]` | - | Additional languages built with `defineLanguage` (merged with the 30 built-in ones) |
|
|
640
719
|
| `mermaidConfig` | `MermaidConfig` | - | Mermaid diagram configuration |
|
|
641
720
|
| `katexConfig` | `KatexOptions \| ((inline: boolean) => KatexOptions)` | - | KaTeX math rendering options |
|
|
642
721
|
| `animation` | `AnimationConfig` | - | Animation configuration for streaming content |
|
|
@@ -753,7 +832,7 @@ Each component supports multiple themeable parts:
|
|
|
753
832
|
|
|
754
833
|
**Links (`a`)**: `base`, `blocked` (for blocked/unsafe links)
|
|
755
834
|
|
|
756
|
-
**Code (`code`)**: `base`, `container`, `header`, `
|
|
835
|
+
**Code (`code`)**: `base`, `container`, `header`, `buttons`, `language`, `line`, `pre`
|
|
757
836
|
|
|
758
837
|
**Inline Code (`inlineCode`)**: `base`
|
|
759
838
|
|
|
@@ -257,4 +257,8 @@
|
|
|
257
257
|
{citationWithSources.length > 1 ? ` +${citationWithSources.length - 1}` : ''}
|
|
258
258
|
</Slot>
|
|
259
259
|
</button>
|
|
260
|
+
{:else}
|
|
261
|
+
<!-- No key matched a `sources` entry: render the literal citation text so it
|
|
262
|
+
isn't silently dropped (e.g. `[123]`, `[ref] [ref2]`). -->
|
|
263
|
+
{token.raw ?? `[${token.keys.join(', ')}]`}
|
|
260
264
|
{/if}
|
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
import { useStreamdown } from '../context.svelte.js';
|
|
3
3
|
import { save } from '../utils/save.js';
|
|
4
4
|
import { useCopy } from '../utils/copy.svelte.js';
|
|
5
|
-
import {
|
|
6
|
-
import { bundledLanguagesInfo } from '../utils/bundledLanguages.js';
|
|
5
|
+
import { highlightLines, languageExtensionMap } from '../utils/hightlighter.svelte.js';
|
|
7
6
|
import type { Tokens } from 'marked';
|
|
8
|
-
import { type ThemedToken } from 'shiki';
|
|
9
|
-
import { untrack } from 'svelte';
|
|
10
7
|
import { checkIcon, copyIcon, downloadIcon } from './icons.js';
|
|
11
8
|
|
|
12
9
|
const {
|
|
@@ -18,11 +15,6 @@
|
|
|
18
15
|
} = $props();
|
|
19
16
|
|
|
20
17
|
const streamdown = useStreamdown();
|
|
21
|
-
const highlighter = HighlighterManager.create(
|
|
22
|
-
bundledLanguagesInfo,
|
|
23
|
-
streamdown.shikiThemes,
|
|
24
|
-
streamdown.shikiLanguages
|
|
25
|
-
);
|
|
26
18
|
|
|
27
19
|
const copy = useCopy({
|
|
28
20
|
get content() {
|
|
@@ -46,13 +38,7 @@
|
|
|
46
38
|
}
|
|
47
39
|
};
|
|
48
40
|
|
|
49
|
-
$
|
|
50
|
-
const theme = streamdown.shikiTheme;
|
|
51
|
-
const lang = token.lang;
|
|
52
|
-
untrack(() => {
|
|
53
|
-
void highlighter.load(theme, lang);
|
|
54
|
-
});
|
|
55
|
-
});
|
|
41
|
+
const lines = $derived(highlightLines(token.text, token.lang, streamdown.highlightLanguages));
|
|
56
42
|
</script>
|
|
57
43
|
|
|
58
44
|
<div
|
|
@@ -84,39 +70,15 @@
|
|
|
84
70
|
{/if}
|
|
85
71
|
</div>
|
|
86
72
|
<div style="height: fit-content; width: 100%;" class={streamdown.theme.code.container}>
|
|
87
|
-
{
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
73
|
+
<pre class={streamdown.theme.code.pre}><code
|
|
74
|
+
>{#each lines as line}<span class={streamdown.theme.code.line}
|
|
75
|
+
>{#if line.length === 0}​{/if}{#each line as t}<span
|
|
76
|
+
class="th-token{t.className ? ` th-${t.className}` : ''}"
|
|
77
|
+
style={streamdown.isMounted ? streamdown.animationTextStyle : ''}
|
|
78
|
+
style:color={streamdown.highlightTheme.tokens[t.className ?? 'token']}
|
|
79
|
+
>{t.value}</span
|
|
80
|
+
>{/each}</span
|
|
81
|
+
>{/each}</code
|
|
82
|
+
></pre>
|
|
97
83
|
</div>
|
|
98
84
|
</div>
|
|
99
|
-
|
|
100
|
-
{#snippet Tokens(lines: ThemedToken[][])}
|
|
101
|
-
{#each lines as tokens}
|
|
102
|
-
<span class={streamdown.theme.code.line}>
|
|
103
|
-
{#each tokens as token}
|
|
104
|
-
<span
|
|
105
|
-
style={streamdown.isMounted ? streamdown.animationTextStyle : ''}
|
|
106
|
-
style:color={token.color}
|
|
107
|
-
style:background-color={token.bgColor}
|
|
108
|
-
>
|
|
109
|
-
{token.content}
|
|
110
|
-
</span>
|
|
111
|
-
{/each}
|
|
112
|
-
</span>
|
|
113
|
-
{/each}
|
|
114
|
-
{/snippet}
|
|
115
|
-
|
|
116
|
-
{#snippet Skeleton(lines: string[])}
|
|
117
|
-
{#each lines as line}
|
|
118
|
-
<span class={streamdown.theme.code.skeleton}>
|
|
119
|
-
{line.trim().length > 0 ? line : '\u200B'}
|
|
120
|
-
</span>
|
|
121
|
-
{/each}
|
|
122
|
-
{/snippet}
|
package/dist/Streamdown.svelte
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
let {
|
|
8
8
|
content = '',
|
|
9
9
|
class: className,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
highlightTheme,
|
|
11
|
+
highlightLanguages,
|
|
12
|
+
highlightThemes,
|
|
13
13
|
parseIncompleteMarkdown,
|
|
14
14
|
defaultOrigin,
|
|
15
15
|
allowedLinkPrefixes = ['*'],
|
|
@@ -36,15 +36,12 @@
|
|
|
36
36
|
...snippets
|
|
37
37
|
}: StreamdownProps<Source> = $props();
|
|
38
38
|
import { useDarkMode } from './utils/darkMode.svelte.js';
|
|
39
|
+
import { resolveHighlightTheme } from './utils/highlightThemes.js';
|
|
39
40
|
|
|
40
41
|
const darkMode = useDarkMode();
|
|
41
42
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
? Object.keys(shikiThemes)[0] || 'github-light'
|
|
45
|
-
: darkMode.current
|
|
46
|
-
? 'github-dark'
|
|
47
|
-
: 'github-light'
|
|
43
|
+
const resolvedHighlightTheme = $derived(
|
|
44
|
+
resolveHighlightTheme(highlightTheme, highlightThemes, darkMode.current)
|
|
48
45
|
);
|
|
49
46
|
|
|
50
47
|
const mermaidThemedTheme = $derived(
|
|
@@ -70,8 +67,8 @@
|
|
|
70
67
|
get allowedImagePrefixes() {
|
|
71
68
|
return allowedImagePrefixes;
|
|
72
69
|
},
|
|
73
|
-
get
|
|
74
|
-
return
|
|
70
|
+
get highlightTheme() {
|
|
71
|
+
return resolvedHighlightTheme;
|
|
75
72
|
},
|
|
76
73
|
get snippets() {
|
|
77
74
|
return snippets;
|
|
@@ -99,11 +96,11 @@
|
|
|
99
96
|
get translations() {
|
|
100
97
|
return translations;
|
|
101
98
|
},
|
|
102
|
-
get
|
|
103
|
-
return
|
|
99
|
+
get highlightLanguages() {
|
|
100
|
+
return highlightLanguages;
|
|
104
101
|
},
|
|
105
|
-
get
|
|
106
|
-
return
|
|
102
|
+
get highlightThemes() {
|
|
103
|
+
return highlightThemes;
|
|
107
104
|
},
|
|
108
105
|
get sources() {
|
|
109
106
|
return sources;
|
package/dist/context.svelte.d.ts
CHANGED
|
@@ -2,11 +2,11 @@ import type { Component, Snippet } from 'svelte';
|
|
|
2
2
|
import type { DeepPartialTheme, Theme } from './theme.js';
|
|
3
3
|
import type { MermaidConfig } from 'mermaid';
|
|
4
4
|
import type { KatexOptions } from 'katex';
|
|
5
|
-
import type {
|
|
6
|
-
import type {
|
|
7
|
-
export interface StreamdownContext extends Omit<StreamdownProps, keyof Snippets | 'class' | 'theme' | '
|
|
5
|
+
import type { HighlightTheme } from '@tanstack/highlight/theme';
|
|
6
|
+
import type { LanguageDefinition } from '@tanstack/highlight';
|
|
7
|
+
export interface StreamdownContext extends Omit<StreamdownProps, keyof Snippets | 'class' | 'theme' | 'highlightTheme' | 'inlineCitationsMode'> {
|
|
8
8
|
snippets: Snippets;
|
|
9
|
-
|
|
9
|
+
highlightTheme: HighlightTheme;
|
|
10
10
|
theme: Theme;
|
|
11
11
|
controls: {
|
|
12
12
|
code: boolean;
|
|
@@ -27,8 +27,9 @@ export declare class StreamdownContext<Source extends Record<string, any> = Reco
|
|
|
27
27
|
isMounted: boolean;
|
|
28
28
|
get animationTextStyle(): string | undefined;
|
|
29
29
|
get animationBlockStyle(): string | undefined;
|
|
30
|
-
constructor(props: Omit<StreamdownProps, keyof Snippets | 'class'> & {
|
|
30
|
+
constructor(props: Omit<StreamdownProps, keyof Snippets | 'class' | 'highlightTheme'> & {
|
|
31
31
|
snippets: Snippets<Source>;
|
|
32
|
+
highlightTheme: HighlightTheme;
|
|
32
33
|
});
|
|
33
34
|
}
|
|
34
35
|
export declare const useStreamdown: () => StreamdownContext<Record<string, any>>;
|
|
@@ -108,9 +109,9 @@ export type StreamdownProps<Source extends Record<string, any> = Record<string,
|
|
|
108
109
|
theme?: DeepPartialTheme;
|
|
109
110
|
baseTheme?: 'tailwind' | 'shadcn';
|
|
110
111
|
mergeTheme?: boolean;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
highlightTheme?: string;
|
|
113
|
+
highlightLanguages?: LanguageDefinition[];
|
|
114
|
+
highlightThemes?: Record<string, HighlightTheme>;
|
|
114
115
|
mermaidConfig?: MermaidConfig;
|
|
115
116
|
katexConfig?: KatexOptions | ((inline: boolean) => KatexOptions);
|
|
116
117
|
translations?: {
|
package/dist/index.d.ts
CHANGED
|
@@ -3,4 +3,5 @@ export { useStreamdown, type StreamdownProps } from './context.svelte.js';
|
|
|
3
3
|
export { theme, shadcnTheme, mergeTheme, type Theme } from './theme.js';
|
|
4
4
|
export { type Extension, type StreamdownToken, lex, parseBlocks } from './marked/index.js';
|
|
5
5
|
export { parseIncompleteMarkdown, type Plugin, IncompleteMarkdownParser } from './utils/parse-incomplete-markdown.js';
|
|
6
|
-
export {
|
|
6
|
+
export { defineLanguage, type LanguageDefinition } from '@tanstack/highlight';
|
|
7
|
+
export type { HighlightTheme } from '@tanstack/highlight/theme';
|
package/dist/index.js
CHANGED
|
@@ -3,4 +3,4 @@ export { useStreamdown } from './context.svelte.js';
|
|
|
3
3
|
export { theme, shadcnTheme, mergeTheme } from './theme.js';
|
|
4
4
|
export { lex, parseBlocks } from './marked/index.js';
|
|
5
5
|
export { parseIncompleteMarkdown, IncompleteMarkdownParser } from './utils/parse-incomplete-markdown.js';
|
|
6
|
-
export {
|
|
6
|
+
export { defineLanguage } from '@tanstack/highlight';
|
package/dist/marked/index.d.ts
CHANGED
|
@@ -31,13 +31,25 @@ export declare const lex: (markdown: string, extensions?: Extension[]) => Stream
|
|
|
31
31
|
* instance (or per simulated stream) and pass it on every call: append-only
|
|
32
32
|
* content updates then re-lex only the last couple of blocks instead of the
|
|
33
33
|
* whole document. Any non-append update falls back to a full parse.
|
|
34
|
+
*
|
|
35
|
+
* Everything here is sized so the append path costs O(live tail), never
|
|
36
|
+
* O(document): `offsets`/`keptBefore` are prefix sums so the seal point is an
|
|
37
|
+
* array lookup, and `blocks` is a persistent array whose tail is truncated and
|
|
38
|
+
* re-pushed instead of being rebuilt with a `filter` callback every chunk.
|
|
34
39
|
*/
|
|
35
40
|
export type ParseBlocksCache = {
|
|
41
|
+
/** the content the cache describes (held by reference, never copied); '' = cold */
|
|
36
42
|
content: string;
|
|
37
43
|
/** every block token's raw (including space/footnote tokens) in document order */
|
|
38
44
|
raws: string[];
|
|
39
45
|
/** parallel to raws: whether the token is part of the rendered block list */
|
|
40
46
|
keep: boolean[];
|
|
47
|
+
/** prefix sums: offsets[i] = start index of raws[i]; offsets[raws.length] = content.length */
|
|
48
|
+
offsets: number[];
|
|
49
|
+
/** prefix sums: keptBefore[i] = number of kept tokens among raws[0..i) */
|
|
50
|
+
keptBefore: number[];
|
|
51
|
+
/** persistent list of kept raws; only its tail is rewritten on append */
|
|
52
|
+
blocks: string[];
|
|
41
53
|
};
|
|
42
54
|
export declare const createParseBlocksCache: () => ParseBlocksCache;
|
|
43
55
|
export declare const parseBlocks: (markdown: string, extensions?: Extension[], cache?: ParseBlocksCache) => string[];
|