svelte-streamdown 2.6.1 → 3.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.
@@ -2,10 +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 { BundledTheme } from 'shiki';
5
+ import type { LanguageInfo } from './utils/bundledLanguages.js';
6
+ import type { ThemeRegistration } from 'shiki';
6
7
  export interface StreamdownContext extends Omit<StreamdownProps, keyof Snippets | 'class' | 'theme' | 'shikiTheme' | 'inlineCitationsMode'> {
7
8
  snippets: Snippets;
8
- shikiTheme: BundledTheme;
9
+ shikiTheme: string;
9
10
  theme: Theme;
10
11
  controls: {
11
12
  code: boolean;
@@ -91,6 +92,7 @@ export type Snippets<Source extends Record<string, any> = Record<string, any>> =
91
92
  };
92
93
  export type StreamdownProps<Source extends Record<string, any> = Record<string, any>> = {
93
94
  streamdown?: StreamdownContext;
95
+ static?: boolean;
94
96
  sources?: {
95
97
  [key: string]: Source;
96
98
  };
@@ -105,8 +107,9 @@ export type StreamdownProps<Source extends Record<string, any> = Record<string,
105
107
  theme?: DeepPartialTheme;
106
108
  baseTheme?: 'tailwind' | 'shadcn';
107
109
  mergeTheme?: boolean;
108
- shikiTheme?: BundledTheme;
109
- shikiPreloadThemes?: BundledTheme[];
110
+ shikiTheme?: string;
111
+ shikiLanguages?: LanguageInfo[];
112
+ shikiThemes?: Record<string, ThemeRegistration>;
110
113
  mermaidConfig?: MermaidConfig;
111
114
  katexConfig?: KatexOptions | ((inline: boolean) => KatexOptions);
112
115
  translations?: {
@@ -159,5 +162,19 @@ export type StreamdownProps<Source extends Record<string, any> = Record<string,
159
162
  children: Snippet;
160
163
  props: any;
161
164
  }, any, any>>;
165
+ components?: {
166
+ code?: Component<{
167
+ token: Tokens.Code;
168
+ id: string;
169
+ }, any, any>;
170
+ mermaid?: Component<{
171
+ token: Tokens.Code;
172
+ id: string;
173
+ }, any, any>;
174
+ math?: Component<{
175
+ token: MathToken;
176
+ id: string;
177
+ }, any, any>;
178
+ };
162
179
  } & Partial<Snippets<Source>>;
163
180
  export {};
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { default as Streamdown } from './Streamdown.svelte';
2
2
  export { useStreamdown, type StreamdownProps } from './context.svelte.js';
3
3
  export { theme, shadcnTheme, mergeTheme, type Theme } from './theme.js';
4
- export { type Extension, type StreamdownToken } from './marked/index.js';
5
- export { lex, parseBlocks } from './marked/index.js';
4
+ export { type Extension, type StreamdownToken, lex, parseBlocks } from './marked/index.js';
5
+ export { parseIncompleteMarkdown, type Plugin, IncompleteMarkdownParser } from './utils/parse-incomplete-markdown.js';
6
+ export { bundledLanguagesInfo, createLanguageSet, type LanguageInfo } from './utils/bundledLanguages.js';
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export { default as Streamdown } from './Streamdown.svelte';
2
2
  export { useStreamdown } from './context.svelte.js';
3
3
  export { theme, shadcnTheme, mergeTheme } from './theme.js';
4
- export {} from './marked/index.js';
5
4
  export { lex, parseBlocks } from './marked/index.js';
5
+ export { parseIncompleteMarkdown, IncompleteMarkdownParser } from './utils/parse-incomplete-markdown.js';
6
+ export { bundledLanguagesInfo, createLanguageSet } from './utils/bundledLanguages.js';
@@ -0,0 +1,8 @@
1
+ export type LanguageInfo = {
2
+ id: string;
3
+ aliases?: string[];
4
+ import: () => Promise<any>;
5
+ };
6
+ export declare const bundledLanguagesInfo: LanguageInfo[];
7
+ export declare function createLanguageSet(languages: LanguageInfo[]): Set<string>;
8
+ export declare const supportedLanguages: Set<string>;
@@ -0,0 +1,143 @@
1
+ export const bundledLanguagesInfo = [
2
+ // Web essentials
3
+ {
4
+ id: 'javascript',
5
+ aliases: ['js'],
6
+ import: () => import('@shikijs/langs/javascript')
7
+ },
8
+ {
9
+ id: 'typescript',
10
+ aliases: ['ts'],
11
+ import: () => import('@shikijs/langs/typescript')
12
+ },
13
+ {
14
+ id: 'html',
15
+ import: () => import('@shikijs/langs/html')
16
+ },
17
+ {
18
+ id: 'css',
19
+ import: () => import('@shikijs/langs/css')
20
+ },
21
+ {
22
+ id: 'json',
23
+ import: () => import('@shikijs/langs/json')
24
+ },
25
+ {
26
+ id: 'jsx',
27
+ import: () => import('@shikijs/langs/jsx')
28
+ },
29
+ {
30
+ id: 'tsx',
31
+ import: () => import('@shikijs/langs/tsx')
32
+ },
33
+ {
34
+ id: 'markdown',
35
+ aliases: ['md'],
36
+ import: () => import('@shikijs/langs/markdown')
37
+ },
38
+ {
39
+ id: 'yaml',
40
+ aliases: ['yml'],
41
+ import: () => import('@shikijs/langs/yaml')
42
+ },
43
+ {
44
+ id: 'xml',
45
+ import: () => import('@shikijs/langs/xml')
46
+ },
47
+ // Backend languages
48
+ {
49
+ id: 'python',
50
+ aliases: ['py'],
51
+ import: () => import('@shikijs/langs/python')
52
+ },
53
+ {
54
+ id: 'java',
55
+ import: () => import('@shikijs/langs/java')
56
+ },
57
+ {
58
+ id: 'go',
59
+ import: () => import('@shikijs/langs/go')
60
+ },
61
+ {
62
+ id: 'rust',
63
+ aliases: ['rs'],
64
+ import: () => import('@shikijs/langs/rust')
65
+ },
66
+ {
67
+ id: 'ruby',
68
+ aliases: ['rb'],
69
+ import: () => import('@shikijs/langs/ruby')
70
+ },
71
+ {
72
+ id: 'php',
73
+ import: () => import('@shikijs/langs/php')
74
+ },
75
+ {
76
+ id: 'c',
77
+ import: () => import('@shikijs/langs/c')
78
+ },
79
+ {
80
+ id: 'cpp',
81
+ aliases: ['c++'],
82
+ import: () => import('@shikijs/langs/cpp')
83
+ },
84
+ {
85
+ id: 'csharp',
86
+ aliases: ['c#', 'cs'],
87
+ import: () => import('@shikijs/langs/csharp')
88
+ },
89
+ {
90
+ id: 'sql',
91
+ import: () => import('@shikijs/langs/sql')
92
+ },
93
+ {
94
+ id: 'swift',
95
+ import: () => import('@shikijs/langs/swift')
96
+ },
97
+ {
98
+ id: 'kotlin',
99
+ aliases: ['kt', 'kts'],
100
+ import: () => import('@shikijs/langs/kotlin')
101
+ },
102
+ // Config/Shell
103
+ {
104
+ id: 'shellscript',
105
+ aliases: ['bash', 'sh', 'shell', 'zsh'],
106
+ import: () => import('@shikijs/langs/shellscript')
107
+ },
108
+ {
109
+ id: 'docker',
110
+ aliases: ['dockerfile'],
111
+ import: () => import('@shikijs/langs/docker')
112
+ },
113
+ {
114
+ id: 'toml',
115
+ import: () => import('@shikijs/langs/toml')
116
+ },
117
+ {
118
+ id: 'graphql',
119
+ aliases: ['gql'],
120
+ import: () => import('@shikijs/langs/graphql')
121
+ },
122
+ {
123
+ id: 'svelte',
124
+ import: () => import('@shikijs/langs/svelte')
125
+ },
126
+ {
127
+ id: 'vue',
128
+ import: () => import('@shikijs/langs/vue')
129
+ }
130
+ ];
131
+ // Helper function to create a Set of all language IDs and aliases from language info
132
+ export function createLanguageSet(languages) {
133
+ const set = new Set();
134
+ languages.forEach((lang) => {
135
+ set.add(lang.id);
136
+ if (lang.aliases) {
137
+ lang.aliases.forEach((alias) => set.add(alias));
138
+ }
139
+ });
140
+ return set;
141
+ }
142
+ // Create a Set of all supported language IDs and aliases for fast lookup (default set)
143
+ export const supportedLanguages = createLanguageSet(bundledLanguagesInfo);
@@ -0,0 +1,3 @@
1
+ export declare const useDarkMode: () => {
2
+ readonly current: boolean;
3
+ };
@@ -0,0 +1,49 @@
1
+ import { onMount } from 'svelte';
2
+ export const useDarkMode = () => {
3
+ let isDark = $state(false);
4
+ const checkTheme = () => {
5
+ const elements = [document.documentElement, document.body];
6
+ // First check for explicit light mode
7
+ for (const element of elements) {
8
+ if (element.classList.contains('light') ||
9
+ element.dataset.theme === 'light' ||
10
+ element.style.colorScheme === 'light') {
11
+ isDark = false;
12
+ return;
13
+ }
14
+ }
15
+ // Then check for dark mode
16
+ for (const element of elements) {
17
+ if (element.classList.contains('dark') ||
18
+ element.dataset.theme === 'dark' ||
19
+ element.style.colorScheme === 'dark') {
20
+ isDark = true;
21
+ return;
22
+ }
23
+ }
24
+ // Default to light mode if no explicit theme is set
25
+ isDark = false;
26
+ };
27
+ onMount(() => {
28
+ // Initial check
29
+ checkTheme();
30
+ // Watch for class, attribute, and style changes on html and body
31
+ const observer = new MutationObserver(() => {
32
+ checkTheme();
33
+ });
34
+ const observerOptions = {
35
+ attributes: true,
36
+ attributeFilter: ['class', 'data-theme', 'style']
37
+ };
38
+ observer.observe(document.documentElement, observerOptions);
39
+ observer.observe(document.body, observerOptions);
40
+ return () => {
41
+ observer.disconnect();
42
+ };
43
+ });
44
+ return {
45
+ get current() {
46
+ return isDark;
47
+ }
48
+ };
49
+ };
@@ -1,32 +1,30 @@
1
- import { type BundledLanguage, type BundledTheme, type HighlighterGeneric, type ThemedToken } from 'shiki';
2
- import { SvelteMap, SvelteSet } from 'svelte/reactivity';
3
- export declare const loadShiki: () => Promise<[any, import("shiki").CreateHighlighterFactory<BundledLanguage, BundledTheme>]>;
4
- export type Highlighter = HighlighterGeneric<BundledLanguage, BundledTheme>;
1
+ import type { ThemedToken, ThemeRegistration } from 'shiki';
2
+ import type { HighlighterCore } from 'shiki/core';
3
+ import { SvelteMap } from 'svelte/reactivity';
4
+ import { type LanguageInfo } from './bundledLanguages.js';
5
+ export type Highlighter = HighlighterCore;
5
6
  declare class HighlighterManager {
6
- loadedLanguages: SvelteMap<BundledLanguage, boolean | Promise<void>>;
7
- loadedThemes: SvelteMap<BundledTheme, boolean | Promise<void>>;
8
- preloadedThemes: SvelteSet<BundledTheme>;
9
- highlighter: Highlighter | Promise<Highlighter> | null;
10
- constructor(preloadedThemes: BundledTheme[]);
7
+ loadedLanguages: SvelteMap<string, boolean | Promise<void>>;
8
+ highlighter: any;
9
+ customLanguages: Set<string>;
10
+ languageLoaders: Map<string, () => Promise<any>>;
11
+ additionalThemes: Record<string, ThemeRegistration>;
12
+ constructor(languages: LanguageInfo[], additionalThemes?: Record<string, ThemeRegistration>, additionalLanguages?: LanguageInfo[]);
11
13
  private loadHighlighter;
12
- private loadTheme;
14
+ private isThemeAvailable;
13
15
  private loadLanguage;
14
16
  private isLanguageSupported;
15
- isReady(theme: BundledTheme, language?: string | undefined): boolean;
16
- /**
17
- * Preloads themes by creating minimal highlighter instances.
18
- * This reduces flickering when switching themes.
19
- */
20
- preloadThemes(highlighter: Highlighter): Promise<void>;
17
+ isReady(theme: string, language: string | undefined): boolean;
21
18
  /**
22
19
  * Ensures the highlighter is ready for the given theme and language.
23
20
  */
24
- load(theme: BundledTheme, language?: string | undefined): Promise<void>;
21
+ load(theme: string, language: string | undefined): Promise<void>;
25
22
  /**
26
23
  * Highlights code synchronously. Must call isReady() first.
24
+ * Returns plaintext tokens for unsupported languages.
27
25
  */
28
- highlightCode(code: string, language: string | undefined, theme: BundledTheme): ThemedToken[][];
29
- static create(preloadedThemes?: BundledTheme[]): HighlighterManager;
26
+ highlightCode(code: string, language: string | undefined, theme: string): ThemedToken[][];
27
+ static create(languages?: LanguageInfo[], additionalThemes?: Record<string, ThemeRegistration>, additionalLanguages?: LanguageInfo[]): HighlighterManager;
30
28
  }
31
29
  export { HighlighterManager };
32
30
  export declare const languageExtensionMap: Record<string, string>;
@@ -1,18 +1,41 @@
1
- import { bundledLanguages } from 'shiki';
2
- import { SvelteMap, SvelteSet } from 'svelte/reactivity';
3
- export const loadShiki = async () => {
4
- return Promise.all([
5
- import('shiki/engine/javascript').then((mod) => mod.createJavaScriptRegexEngine({ forgiving: true })),
6
- import('shiki').then((mod) => mod.createHighlighter)
7
- ]);
1
+ import { SvelteMap } from 'svelte/reactivity';
2
+ import { supportedLanguages, createLanguageSet, bundledLanguagesInfo } from './bundledLanguages.js';
3
+ import { createHighlighterCore } from 'shiki/core';
4
+ import { createJavaScriptRegexEngine } from 'shiki/engine/javascript';
5
+ // Import default themes directly
6
+ import githubDark from '@shikijs/themes/github-dark';
7
+ import githubLight from '@shikijs/themes/github-light';
8
+ // Default themes that are always loaded
9
+ const DEFAULT_THEMES = {
10
+ 'github-dark': githubDark,
11
+ 'github-light': githubLight
8
12
  };
9
13
  class HighlighterManager {
10
14
  loadedLanguages = new SvelteMap();
11
- loadedThemes = new SvelteMap();
12
- preloadedThemes = new SvelteSet();
13
15
  highlighter = $state(null);
14
- constructor(preloadedThemes) {
15
- preloadedThemes.forEach((theme) => this.preloadedThemes.add(theme));
16
+ customLanguages;
17
+ languageLoaders;
18
+ additionalThemes;
19
+ constructor(languages, additionalThemes, additionalLanguages) {
20
+ // Store additional themes (will be loaded with highlighter)
21
+ this.additionalThemes = additionalThemes || {};
22
+ // Merge languages: default + additional
23
+ const allLanguages = additionalLanguages ? [...languages, ...additionalLanguages] : languages;
24
+ this.languageLoaders = new Map();
25
+ allLanguages.forEach((l) => {
26
+ this.languageLoaders.set(l.id, l.import);
27
+ if (l.aliases) {
28
+ l.aliases.forEach((alias) => this.languageLoaders.set(alias, l.import));
29
+ }
30
+ });
31
+ // Build custom languages set for validation
32
+ if (additionalLanguages) {
33
+ const additionalSet = createLanguageSet(additionalLanguages);
34
+ this.customLanguages = new Set([...supportedLanguages, ...additionalSet]);
35
+ }
36
+ else {
37
+ this.customLanguages = supportedLanguages;
38
+ }
16
39
  if (typeof window !== 'undefined') {
17
40
  Object.assign(window, {
18
41
  STREAMDOWN_HIGHLIGHTER: this
@@ -24,17 +47,25 @@ class HighlighterManager {
24
47
  return this.highlighter;
25
48
  }
26
49
  else if (!this.highlighter) {
27
- this.highlighter = new Promise((resolve, reject) => {
28
- loadShiki().then(([engine, createHighlighter]) => {
29
- return createHighlighter({
30
- themes: [],
50
+ this.highlighter = new Promise(async (resolve, reject) => {
51
+ try {
52
+ const engine = createJavaScriptRegexEngine({ forgiving: true });
53
+ // Load default themes + any additional themes immediately
54
+ const allThemes = [
55
+ ...Object.values(DEFAULT_THEMES),
56
+ ...Object.values(this.additionalThemes)
57
+ ];
58
+ const highlighter = await createHighlighterCore({
59
+ themes: allThemes,
31
60
  langs: [],
32
61
  engine
33
- }).then((highlighter) => {
34
- this.highlighter = highlighter;
35
- resolve(highlighter);
36
62
  });
37
- });
63
+ this.highlighter = highlighter;
64
+ resolve(highlighter);
65
+ }
66
+ catch (error) {
67
+ reject(error);
68
+ }
38
69
  });
39
70
  return this.highlighter;
40
71
  }
@@ -42,94 +73,143 @@ class HighlighterManager {
42
73
  return this.highlighter;
43
74
  }
44
75
  }
45
- async loadTheme(theme, highlighter) {
46
- const themeLoader = this.loadedThemes.get(theme);
47
- if (themeLoader instanceof Promise) {
48
- await themeLoader;
49
- }
50
- else {
51
- const themeLoaderPromise = highlighter.loadTheme(theme).then(() => {
52
- this.loadedThemes.set(theme, true);
53
- });
54
- this.loadedThemes.set(theme, themeLoaderPromise);
55
- await themeLoaderPromise;
56
- }
76
+ isThemeAvailable(theme) {
77
+ return theme in DEFAULT_THEMES || theme in this.additionalThemes;
57
78
  }
58
79
  async loadLanguage(language, highlighter) {
80
+ // Skip loading if language is not supported
81
+ if (!this.isLanguageSupported(language)) {
82
+ return;
83
+ }
59
84
  const languageLoader = this.loadedLanguages.get(language);
60
85
  if (languageLoader instanceof Promise) {
61
86
  await languageLoader;
62
87
  }
63
- else {
64
- const languageLoaderPromise = highlighter.loadLanguage(language).then(() => {
88
+ else if (!languageLoader) {
89
+ const loader = this.languageLoaders.get(language);
90
+ if (!loader) {
91
+ // Language not available, mark as failed
92
+ this.loadedLanguages.set(language, false);
93
+ return;
94
+ }
95
+ const languageLoaderPromise = loader()
96
+ .then((langModule) => {
97
+ const langObj = langModule.default || langModule;
98
+ return highlighter.loadLanguage(langObj);
99
+ })
100
+ .then(() => {
65
101
  this.loadedLanguages.set(language, true);
102
+ })
103
+ .catch((err) => {
104
+ this.loadedLanguages.set(language, false);
105
+ throw err;
66
106
  });
67
107
  this.loadedLanguages.set(language, languageLoaderPromise);
68
108
  await languageLoaderPromise;
69
109
  }
70
110
  }
71
111
  isLanguageSupported = (language) => {
72
- return Object.hasOwn(bundledLanguages, language);
112
+ return this.customLanguages.has(language);
73
113
  };
74
- isReady(theme, language = 'bash') {
114
+ isReady(theme, language) {
115
+ // Check if theme is available
116
+ if (!this.isThemeAvailable(theme)) {
117
+ return false;
118
+ }
119
+ // For unsupported languages, we don't need to load anything (will use plaintext)
120
+ if (!language || !this.isLanguageSupported(language)) {
121
+ return !!this.highlighter && !(this.highlighter instanceof Promise);
122
+ }
75
123
  return (!!this.highlighter &&
76
124
  !(this.highlighter instanceof Promise) &&
77
- this.loadedThemes.get(theme) === true &&
78
- this.loadedLanguages.get(this.isLanguageSupported(language) ? language : 'bash') === true);
79
- }
80
- /**
81
- * Preloads themes by creating minimal highlighter instances.
82
- * This reduces flickering when switching themes.
83
- */
84
- async preloadThemes(highlighter) {
85
- await Promise.all(Array.from(this.preloadedThemes).map((theme) => this.loadTheme(theme, highlighter)));
125
+ this.loadedLanguages.get(language) === true);
86
126
  }
87
127
  /**
88
128
  * Ensures the highlighter is ready for the given theme and language.
89
129
  */
90
- async load(theme, language = 'bash') {
91
- const themeLoader = this.loadedThemes.get(theme);
92
- const languageLoader = this.loadedLanguages.get(this.isLanguageSupported(language) ? language : 'bash');
130
+ async load(theme, language) {
131
+ // For unsupported languages, only need to load highlighter (themes are pre-loaded)
132
+ if (!language || !this.isLanguageSupported(language)) {
133
+ if (this.highlighter !== null && !(this.highlighter instanceof Promise)) {
134
+ return;
135
+ }
136
+ await this.loadHighlighter();
137
+ return;
138
+ }
139
+ const languageLoader = this.loadedLanguages.get(language);
93
140
  if (this.highlighter !== null &&
94
141
  !(this.highlighter instanceof Promise) &&
95
- themeLoader === true &&
96
142
  languageLoader === true) {
97
143
  return;
98
144
  }
99
145
  const highlighter = await this.loadHighlighter();
100
- await Promise.all([
101
- this.loadTheme(theme, highlighter),
102
- this.loadLanguage(this.isLanguageSupported(language) ? language : 'bash', highlighter)
103
- ]);
104
- await this.preloadThemes(highlighter);
146
+ await this.loadLanguage(language, highlighter);
105
147
  }
106
148
  /**
107
149
  * Highlights code synchronously. Must call isReady() first.
150
+ * Returns plaintext tokens for unsupported languages.
108
151
  */
109
- highlightCode(code, language = 'bash', theme) {
152
+ highlightCode(code, language, theme) {
110
153
  try {
111
- // const highlighter = this.highlighters.get(`${theme}:${language}`);
112
154
  const highlighter = this.highlighter;
113
155
  if (!highlighter || highlighter instanceof Promise) {
114
- return [];
156
+ // Return plaintext tokens when highlighter is not ready
157
+ return code.split('\n').map((line) => [
158
+ {
159
+ content: line,
160
+ color: undefined,
161
+ bgColor: undefined
162
+ }
163
+ ]);
164
+ }
165
+ // For unsupported languages, return plaintext tokens
166
+ if (!language || !this.isLanguageSupported(language)) {
167
+ return code.split('\n').map((line) => [
168
+ {
169
+ content: line,
170
+ color: undefined,
171
+ bgColor: undefined
172
+ }
173
+ ]);
115
174
  }
116
175
  const tokens = highlighter.codeToTokensBase(code, {
117
- lang: this.isLanguageSupported(language) ? language : 'bash',
176
+ lang: language,
118
177
  theme
119
178
  });
120
179
  return tokens;
121
180
  }
122
181
  catch (error) {
123
- return [];
182
+ // Return plaintext tokens on error
183
+ return code.split('\n').map((line) => [
184
+ {
185
+ content: line,
186
+ color: undefined,
187
+ bgColor: undefined
188
+ }
189
+ ]);
124
190
  }
125
191
  }
126
- static create(preloadedThemes = []) {
192
+ static create(languages = bundledLanguagesInfo, additionalThemes, additionalLanguages) {
127
193
  if (typeof window !== 'undefined' && 'STREAMDOWN_HIGHLIGHTER' in window) {
128
194
  const previousHighlighter = window.STREAMDOWN_HIGHLIGHTER;
129
- preloadedThemes.forEach((theme) => previousHighlighter.preloadedThemes.add(theme));
195
+ // Merge additional themes
196
+ if (additionalThemes) {
197
+ Object.assign(previousHighlighter.additionalThemes, additionalThemes);
198
+ }
199
+ // Merge additional languages with existing set
200
+ if (additionalLanguages) {
201
+ additionalLanguages.forEach((lang) => {
202
+ previousHighlighter.languageLoaders.set(lang.id, lang.import);
203
+ if (lang.aliases) {
204
+ lang.aliases.forEach((alias) => previousHighlighter.languageLoaders.set(alias, lang.import));
205
+ }
206
+ });
207
+ const additionalSet = createLanguageSet(additionalLanguages);
208
+ additionalSet.forEach((lang) => previousHighlighter.customLanguages.add(lang));
209
+ }
130
210
  return previousHighlighter;
131
211
  }
132
- return new HighlighterManager(preloadedThemes);
212
+ return new HighlighterManager(languages, additionalThemes, additionalLanguages);
133
213
  }
134
214
  }
135
215
  // Export the class for those who want to create their own instances
@@ -1 +1,53 @@
1
+ export interface Plugin {
2
+ name: string;
3
+ pattern?: RegExp;
4
+ handler?: (payload: HandlerPayload) => string;
5
+ skipInBlockTypes?: string[];
6
+ preprocess?: (payload: HookPayload) => string | {
7
+ text: string;
8
+ state: Partial<ParseState>;
9
+ };
10
+ postprocess?: (payload: HookPayload) => string;
11
+ }
12
+ interface HookPayload {
13
+ text: string;
14
+ state: ParseState;
15
+ setState: (state: Partial<ParseState>) => void;
16
+ }
17
+ interface HandlerPayload {
18
+ line: string;
19
+ text: string;
20
+ match: RegExpMatchArray;
21
+ state: ParseState;
22
+ setState: (state: Partial<ParseState>) => void;
23
+ }
24
+ interface ParseState {
25
+ currentLine: number;
26
+ context: 'normal' | 'list' | 'blockquote' | 'descriptionList';
27
+ blockingContexts: Set<'code' | 'math' | 'center' | 'right'>;
28
+ lineContexts?: Array<{
29
+ code: boolean;
30
+ math: boolean;
31
+ center: boolean;
32
+ right: boolean;
33
+ }>;
34
+ fenceInfo?: string;
35
+ mdxUnclosedTags?: Array<{
36
+ tagName: string;
37
+ lineIndex: number;
38
+ }>;
39
+ mdxLineStates?: Array<{
40
+ inMdx: boolean;
41
+ incompletePositions: number[];
42
+ }>;
43
+ }
44
+ export declare class IncompleteMarkdownParser {
45
+ private plugins;
46
+ private state;
47
+ setState: (state: Partial<ParseState>) => void;
48
+ constructor(plugins?: Plugin[]);
49
+ parse(text: string): string;
50
+ static createDefaultPlugins(): Plugin[];
51
+ }
1
52
  export declare const parseIncompleteMarkdown: (text: string) => string;
53
+ export {};
@@ -1,4 +1,4 @@
1
- class IncompleteMarkdownParser {
1
+ export class IncompleteMarkdownParser {
2
2
  plugins = [];
3
3
  state = {
4
4
  currentLine: 0,