starlight-obsidian 0.13.0 → 0.14.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/CHANGELOG.md +12 -0
- package/index.ts +5 -5
- package/libs/{plugin.ts → error.ts} +1 -1
- package/libs/integration.ts +4 -9
- package/libs/markdown.ts +29 -0
- package/libs/obsidian.ts +3 -3
- package/libs/processor.ts +68 -0
- package/libs/rehype.ts +6 -25
- package/libs/satteri.ts +86 -0
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# starlight-obsidian
|
|
2
2
|
|
|
3
|
+
## 0.14.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#77](https://github.com/HiDeoo/starlight-obsidian/pull/77) [`5818330`](https://github.com/HiDeoo/starlight-obsidian/commit/5818330456dd11d03f9787f4d9b6a4e68359d02d) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Adds support for Astro v7, drops support for Astro v6.
|
|
8
|
+
|
|
9
|
+
⚠️ **BREAKING CHANGE:** The minimum supported version of Starlight is now `0.41.0`.
|
|
10
|
+
|
|
11
|
+
Please follow the [upgrade guide](https://github.com/withastro/starlight/releases/tag/%40astrojs%2Fstarlight%400.41.0) to update your project.
|
|
12
|
+
|
|
13
|
+
- [#77](https://github.com/HiDeoo/starlight-obsidian/pull/77) [`5818330`](https://github.com/HiDeoo/starlight-obsidian/commit/5818330456dd11d03f9787f4d9b6a4e68359d02d) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Adds support for the Sätteri Markdown processor.
|
|
14
|
+
|
|
3
15
|
## 0.13.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/index.ts
CHANGED
|
@@ -4,10 +4,10 @@ import type { StarlightPlugin, StarlightUserConfig } from '@astrojs/starlight/ty
|
|
|
4
4
|
import type { AstroIntegrationLogger } from 'astro'
|
|
5
5
|
import { z } from 'astro/zod'
|
|
6
6
|
|
|
7
|
+
import { throwPluginError } from './libs/error'
|
|
7
8
|
import { starlightObsidianIntegration } from './libs/integration'
|
|
8
9
|
import { getObsidianPaths, getVault } from './libs/obsidian'
|
|
9
10
|
import { stripLeadingAndTrailingSlashes } from './libs/path'
|
|
10
|
-
import { throwUserError } from './libs/plugin'
|
|
11
11
|
import {
|
|
12
12
|
addObsidianFiles,
|
|
13
13
|
getSidebarEntriesPlaceholder,
|
|
@@ -158,7 +158,7 @@ function makeStarlightObsidianPlugin(
|
|
|
158
158
|
})
|
|
159
159
|
|
|
160
160
|
if (isUsingRemovedSidebarGroupConfig) {
|
|
161
|
-
|
|
161
|
+
throwPluginError(
|
|
162
162
|
'The `sidebar.label` and `sidebar.collapsed` options have been removed.',
|
|
163
163
|
'Create a group in your Starlight sidebar configuration, set the `label` and `collapsed` options on that group, and add `obsidianSidebarEntries` to its `items` array.\nFor more information see https://starlight.astro.build/guides/sidebar/#groups',
|
|
164
164
|
)
|
|
@@ -169,13 +169,13 @@ function makeStarlightObsidianPlugin(
|
|
|
169
169
|
)
|
|
170
170
|
|
|
171
171
|
if (isUsingDeprecatedCopyStarlightFrontmatter) {
|
|
172
|
-
|
|
172
|
+
throwPluginError(
|
|
173
173
|
'The `copyStarlightFrontmatter` option has been deprecated in favor of the `copyFrontmatter` option.',
|
|
174
174
|
'For more information see https://starlight-obsidian.vercel.app/configuration/#copyfrontmatter',
|
|
175
175
|
)
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
throwPluginError(
|
|
179
179
|
`Invalid starlight-obsidian configuration:
|
|
180
180
|
|
|
181
181
|
${z.prettifyError(parsedConfig.error)}
|
|
@@ -231,7 +231,7 @@ ${z.prettifyError(parsedConfig.error)}
|
|
|
231
231
|
} catch (error) {
|
|
232
232
|
logger.error(error instanceof Error ? error.message : String(error))
|
|
233
233
|
|
|
234
|
-
|
|
234
|
+
throwPluginError(`Failed to generate Starlight pages from Obsidian vault at '${config.vault}'.`)
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
|
package/libs/integration.ts
CHANGED
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
import type { AstroIntegration } from 'astro'
|
|
2
|
-
import rehypeKatex from 'rehype-katex'
|
|
3
|
-
import remarkMath from 'remark-math'
|
|
4
2
|
|
|
5
3
|
import type { StarlightObsidianConfig } from '..'
|
|
6
4
|
|
|
7
|
-
import {
|
|
5
|
+
import { applyMarkdownPlugin } from './processor'
|
|
8
6
|
import { vitePluginStarlightObsidianConfig } from './vite'
|
|
9
7
|
|
|
10
8
|
export function starlightObsidianIntegration(config: StarlightObsidianConfig): AstroIntegration {
|
|
11
9
|
return {
|
|
12
10
|
name: 'starlight-obsidian-integration',
|
|
13
11
|
hooks: {
|
|
14
|
-
'astro:config:setup': ({ updateConfig }) => {
|
|
15
|
-
|
|
16
|
-
markdown: {
|
|
17
|
-
rehypePlugins: [rehypeStarlightObsidian, rehypeKatex],
|
|
12
|
+
'astro:config:setup': ({ config: astroConfig, updateConfig }) => {
|
|
13
|
+
applyMarkdownPlugin(astroConfig.markdown.processor, config)
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
},
|
|
15
|
+
updateConfig({
|
|
21
16
|
vite: {
|
|
22
17
|
plugins: [vitePluginStarlightObsidianConfig(config)],
|
|
23
18
|
},
|
package/libs/markdown.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ElementContent } from 'hast'
|
|
2
|
+
import type { Literal } from 'mdast'
|
|
1
3
|
import { fromMarkdown } from 'mdast-util-from-markdown'
|
|
2
4
|
import { remark } from 'remark'
|
|
3
5
|
import remarkFrontmatter from 'remark-frontmatter'
|
|
@@ -7,6 +9,8 @@ import { VFile } from 'vfile'
|
|
|
7
9
|
|
|
8
10
|
import { remarkStarlightObsidian, type TransformContext } from './remark'
|
|
9
11
|
|
|
12
|
+
const blockIdentifierRegex = /(?<identifier> *\^(?<name>[\w-]+))$/
|
|
13
|
+
|
|
10
14
|
let processor: ReturnType<typeof remark> | undefined
|
|
11
15
|
|
|
12
16
|
export async function transformMarkdownToString(
|
|
@@ -49,9 +53,34 @@ function getVFile(filePath: string, markdown: string, context: TransformContext)
|
|
|
49
53
|
})
|
|
50
54
|
}
|
|
51
55
|
|
|
56
|
+
export function isNodeWithValue(node: ElementContent | undefined): node is NodeWithValue {
|
|
57
|
+
return node !== undefined && 'value' in node
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function getBlockIdentifier(node: NodeWithValue): { length: number; name: string } | undefined {
|
|
61
|
+
const match = blockIdentifierRegex.exec(node.value)
|
|
62
|
+
const identifier = match?.groups?.['identifier']
|
|
63
|
+
const name = match?.groups?.['name']
|
|
64
|
+
|
|
65
|
+
if (!identifier || !name) {
|
|
66
|
+
return undefined
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return { length: identifier.length, name }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function getLastContentChild(children: ElementContent[]) {
|
|
73
|
+
const index = children.findLastIndex((child) => child.type !== 'text' || child.value.trim().length > 0)
|
|
74
|
+
if (index === -1) return undefined
|
|
75
|
+
|
|
76
|
+
return { child: children[index], index }
|
|
77
|
+
}
|
|
78
|
+
|
|
52
79
|
interface TransformResult {
|
|
53
80
|
aliases: string[] | undefined
|
|
54
81
|
content: string
|
|
55
82
|
skip: boolean
|
|
56
83
|
type: 'markdown' | 'mdx'
|
|
57
84
|
}
|
|
85
|
+
|
|
86
|
+
export type NodeWithValue = ElementContent & Literal
|
package/libs/obsidian.ts
CHANGED
|
@@ -9,9 +9,9 @@ import yaml from 'yaml'
|
|
|
9
9
|
|
|
10
10
|
import type { StarlightObsidianConfig } from '..'
|
|
11
11
|
|
|
12
|
+
import { throwPluginError } from './error'
|
|
12
13
|
import { isDirectory, isFile } from './fs'
|
|
13
14
|
import { getExtension, isAnchor, slashify, slugifyPath, stripExtension } from './path'
|
|
14
|
-
import { throwUserError } from './plugin'
|
|
15
15
|
import { isAssetFile } from './starlight'
|
|
16
16
|
|
|
17
17
|
const obsidianAppConfigSchema = z.object({
|
|
@@ -48,11 +48,11 @@ export async function getVault(config: StarlightObsidianConfig): Promise<Vault>
|
|
|
48
48
|
const vaultPath = path.resolve(config.vault)
|
|
49
49
|
|
|
50
50
|
if (!(await isDirectory(vaultPath))) {
|
|
51
|
-
|
|
51
|
+
throwPluginError(`The provided vault path is not a directory.\n> Provided path: ${vaultPath}`)
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
if (!(await isVaultDirectory(config, vaultPath))) {
|
|
55
|
-
|
|
55
|
+
throwPluginError(
|
|
56
56
|
`The provided vault path is not a valid Obsidian vault directory and does not include an '.obsidian/app.json' file.\n> Provided path: ${vaultPath}`,
|
|
57
57
|
)
|
|
58
58
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { katex as satteriKatex } from '@nullpinter/satteri-katex'
|
|
2
|
+
import type { AstroConfig } from 'astro'
|
|
3
|
+
import rehypeKatex from 'rehype-katex'
|
|
4
|
+
import remarkMath from 'remark-math'
|
|
5
|
+
|
|
6
|
+
import type { StarlightObsidianConfig } from '..'
|
|
7
|
+
|
|
8
|
+
import { throwPluginError } from './error'
|
|
9
|
+
import { rehypeStarlightObsidian } from './rehype'
|
|
10
|
+
import { satteriStarlightObsidian } from './satteri'
|
|
11
|
+
|
|
12
|
+
export function applyMarkdownPlugin(processor: MarkdownProcessor, config: StarlightObsidianConfig) {
|
|
13
|
+
if (isSatteriProcessor(processor)) {
|
|
14
|
+
processor.options.features ??= {}
|
|
15
|
+
processor.options.features.math = {
|
|
16
|
+
...(typeof processor.options.features.math === 'object' ? processor.options.features.math : {}),
|
|
17
|
+
singleDollarTextMath: config.math.singleDollarTextMath,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
processor.options.mdastPlugins.push(satteriKatex())
|
|
21
|
+
processor.options.hastPlugins.push(satteriStarlightObsidian)
|
|
22
|
+
} else if (isUnifiedProcessor(processor)) {
|
|
23
|
+
processor.options.remarkPlugins.push([remarkMath, { singleDollarTextMath: config.math.singleDollarTextMath }])
|
|
24
|
+
processor.options.rehypePlugins.push([rehypeStarlightObsidian], [rehypeKatex])
|
|
25
|
+
} else {
|
|
26
|
+
throwPluginError("The configured 'markdown.processor' is not supported by the starlight-obsidian plugin.")
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isSatteriProcessor(processor: unknown): processor is SatteriMarkdownProcessor {
|
|
31
|
+
if (typeof processor !== 'object' || processor === null) return false
|
|
32
|
+
const candidate = processor as { name?: unknown; options?: { hastPlugins?: unknown; mdastPlugins?: unknown[] } }
|
|
33
|
+
return (
|
|
34
|
+
candidate.name === 'satteri' &&
|
|
35
|
+
Array.isArray(candidate.options?.hastPlugins) &&
|
|
36
|
+
Array.isArray(candidate.options.mdastPlugins)
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isUnifiedProcessor(processor: unknown): processor is UnifiedMarkdownProcessor {
|
|
41
|
+
if (typeof processor !== 'object' || processor === null) return false
|
|
42
|
+
const candidate = processor as { name?: unknown; options?: { rehypePlugins?: unknown; remarkPlugins?: unknown[] } }
|
|
43
|
+
return (
|
|
44
|
+
candidate.name === 'unified' &&
|
|
45
|
+
Array.isArray(candidate.options?.rehypePlugins) &&
|
|
46
|
+
Array.isArray(candidate.options.remarkPlugins)
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
type MarkdownProcessor = NonNullable<AstroConfig['markdown']['processor']>
|
|
51
|
+
|
|
52
|
+
interface SatteriMarkdownProcessor {
|
|
53
|
+
name: string
|
|
54
|
+
options: { features?: SatteriFeatures; hastPlugins: unknown[]; mdastPlugins: unknown[] }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface SatteriFeatures {
|
|
58
|
+
math?:
|
|
59
|
+
| boolean
|
|
60
|
+
| {
|
|
61
|
+
singleDollarTextMath?: boolean
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface UnifiedMarkdownProcessor {
|
|
66
|
+
name: string
|
|
67
|
+
options: { rehypePlugins: unknown[]; remarkPlugins: unknown[] }
|
|
68
|
+
}
|
package/libs/rehype.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { Element, ElementContent, Root } from 'hast'
|
|
2
|
-
import type { Literal } from 'mdast'
|
|
3
2
|
import { CONTINUE, SKIP, visit } from 'unist-util-visit'
|
|
4
3
|
|
|
5
|
-
|
|
4
|
+
import { getBlockIdentifier, getLastContentChild, isNodeWithValue } from './markdown'
|
|
6
5
|
|
|
7
6
|
export function rehypeStarlightObsidian() {
|
|
8
7
|
return function transformer(tree: Root) {
|
|
@@ -12,7 +11,7 @@ export function rehypeStarlightObsidian() {
|
|
|
12
11
|
// Handle blockqoutes first as they are block which can contain paragraphs or list items and we want to hoist
|
|
13
12
|
// the IDs to the blockquote element.
|
|
14
13
|
if (node.tagName === 'blockquote') {
|
|
15
|
-
const lastChild = node.children
|
|
14
|
+
const lastChild = getLastContentChild(node.children)?.child
|
|
16
15
|
|
|
17
16
|
if (
|
|
18
17
|
lastChild?.type !== 'element' ||
|
|
@@ -21,15 +20,15 @@ export function rehypeStarlightObsidian() {
|
|
|
21
20
|
return CONTINUE
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
const lastGrandChild = lastChild.children
|
|
23
|
+
const lastGrandChild = getLastContentChild(lastChild.children)?.child
|
|
25
24
|
|
|
26
25
|
if (lastChild.tagName === 'p') {
|
|
27
26
|
return transformBlockIdentifier(node, lastGrandChild)
|
|
28
27
|
} else if (lastGrandChild?.type === 'element' && lastGrandChild.tagName === 'li') {
|
|
29
|
-
return transformBlockIdentifier(node, lastGrandChild.children
|
|
28
|
+
return transformBlockIdentifier(node, getLastContentChild(lastGrandChild.children)?.child)
|
|
30
29
|
}
|
|
31
30
|
} else if (node.tagName === 'p' || node.tagName === 'li') {
|
|
32
|
-
return transformBlockIdentifier(node, node.children
|
|
31
|
+
return transformBlockIdentifier(node, getLastContentChild(node.children)?.child)
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
return CONTINUE
|
|
@@ -42,7 +41,7 @@ function transformBlockIdentifier(reference: Element, node: ElementContent | und
|
|
|
42
41
|
return CONTINUE
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
const identifier =
|
|
44
|
+
const identifier = getBlockIdentifier(node)
|
|
46
45
|
|
|
47
46
|
if (!identifier) {
|
|
48
47
|
return CONTINUE
|
|
@@ -53,21 +52,3 @@ function transformBlockIdentifier(reference: Element, node: ElementContent | und
|
|
|
53
52
|
|
|
54
53
|
return SKIP
|
|
55
54
|
}
|
|
56
|
-
|
|
57
|
-
function isNodeWithValue(node: ElementContent | undefined): node is NodeWithValue {
|
|
58
|
-
return node !== undefined && 'value' in node
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function getBlockIdentifer(node: NodeWithValue): { length: number; name: string } | undefined {
|
|
62
|
-
const match = blockIdentifierRegex.exec(node.value)
|
|
63
|
-
const identifier = match?.groups?.['identifier']
|
|
64
|
-
const name = match?.groups?.['name']
|
|
65
|
-
|
|
66
|
-
if (!identifier || !name) {
|
|
67
|
-
return undefined
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return { length: identifier.length, name }
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
type NodeWithValue = ElementContent & Literal
|
package/libs/satteri.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { Element, ElementContent, Parents } from 'hast'
|
|
2
|
+
import { defineHastPlugin, type HastPluginDefinition, type HastVisitorContext } from 'satteri'
|
|
3
|
+
|
|
4
|
+
import { getBlockIdentifier, getLastContentChild, isNodeWithValue } from './markdown'
|
|
5
|
+
|
|
6
|
+
export function satteriStarlightObsidian(): HastPluginDefinition {
|
|
7
|
+
return defineHastPlugin({
|
|
8
|
+
name: 'starlight-obsidian',
|
|
9
|
+
element: {
|
|
10
|
+
filter: ['blockquote', 'p', 'li'],
|
|
11
|
+
visit(node, ctx) {
|
|
12
|
+
if (node.tagName === 'blockquote') {
|
|
13
|
+
const lastChild = getLastContentChild(node.children)?.child
|
|
14
|
+
|
|
15
|
+
if (
|
|
16
|
+
lastChild?.type !== 'element' ||
|
|
17
|
+
!(lastChild.tagName === 'p' || lastChild.tagName === 'ul' || lastChild.tagName === 'ol')
|
|
18
|
+
) {
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const lastGrandChild = getLastContentChild(lastChild.children)?.child
|
|
23
|
+
|
|
24
|
+
if (lastChild.tagName === 'p') {
|
|
25
|
+
transformBlockIdentifier(node, lastGrandChild, ctx)
|
|
26
|
+
} else if (lastGrandChild?.type === 'element' && lastGrandChild.tagName === 'li') {
|
|
27
|
+
transformBlockIdentifier(node, getLastContentChild(lastGrandChild.children)?.child, ctx)
|
|
28
|
+
}
|
|
29
|
+
} else if (node.tagName === 'p' || node.tagName === 'li') {
|
|
30
|
+
if (isInsideBlockquoteWithIdentifier(node, ctx)) return
|
|
31
|
+
|
|
32
|
+
transformBlockIdentifier(node, getLastContentChild(node.children)?.child, ctx)
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function transformBlockIdentifier(reference: Element, node: ElementContent | undefined, ctx: HastVisitorContext) {
|
|
40
|
+
if (!isNodeWithValue(node)) return
|
|
41
|
+
|
|
42
|
+
const identifier = getBlockIdentifier(node)
|
|
43
|
+
if (!identifier) return
|
|
44
|
+
|
|
45
|
+
ctx.setProperty(node, 'value', node.value.slice(0, identifier.length * -1))
|
|
46
|
+
ctx.setProperty(reference, 'id', `block-${identifier.name}`)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isInsideBlockquoteWithIdentifier(node: Element, ctx: HastVisitorContext) {
|
|
50
|
+
let parent: Parents | undefined = ctx.parent(node)
|
|
51
|
+
|
|
52
|
+
while (parent !== undefined) {
|
|
53
|
+
if (parent.type === 'element' && parent.tagName === 'blockquote') {
|
|
54
|
+
return blockquoteHasIdentifier(parent)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
parent = ctx.parent(parent)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function blockquoteHasIdentifier(blockquote: Element) {
|
|
64
|
+
const lastChild = getLastContentChild(blockquote.children)?.child
|
|
65
|
+
|
|
66
|
+
if (
|
|
67
|
+
lastChild?.type !== 'element' ||
|
|
68
|
+
!(lastChild.tagName === 'p' || lastChild.tagName === 'ul' || lastChild.tagName === 'ol')
|
|
69
|
+
) {
|
|
70
|
+
return false
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const lastGrandChild = getLastContentChild(lastChild.children)?.child
|
|
74
|
+
|
|
75
|
+
if (lastChild.tagName === 'p') {
|
|
76
|
+
return isNodeWithValue(lastGrandChild) && getBlockIdentifier(lastGrandChild) !== undefined
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (lastGrandChild?.type !== 'element' || lastGrandChild.tagName !== 'li') {
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const lastListItemChild = getLastContentChild(lastGrandChild.children)?.child
|
|
84
|
+
|
|
85
|
+
return isNodeWithValue(lastListItemChild) && getBlockIdentifier(lastListItemChild) !== undefined
|
|
86
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "starlight-obsidian",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Starlight plugin to publish Obsidian vaults.",
|
|
6
6
|
"author": "HiDeoo <github@hideoo.dev> (https://hideoo.dev)",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@astro-community/astro-embed-twitter": "^0.5.11",
|
|
21
21
|
"@astro-community/astro-embed-youtube": "^0.5.10",
|
|
22
|
+
"@nullpinter/satteri-katex": "^0.1.2",
|
|
22
23
|
"decode-uri-component": "^0.4.1",
|
|
23
24
|
"github-slugger": "^2.0.0",
|
|
24
25
|
"globby": "^16.1.1",
|
|
@@ -35,6 +36,7 @@
|
|
|
35
36
|
"remark-frontmatter": "^5.0.0",
|
|
36
37
|
"remark-gfm": "^4.0.1",
|
|
37
38
|
"remark-math": "^6.0.0",
|
|
39
|
+
"satteri": "^0.9.3",
|
|
38
40
|
"unist-util-visit": "^5.1.0",
|
|
39
41
|
"vfile": "^6.0.3",
|
|
40
42
|
"yaml": "^2.8.2"
|
|
@@ -48,7 +50,7 @@
|
|
|
48
50
|
"vitest": "^4.0.18"
|
|
49
51
|
},
|
|
50
52
|
"peerDependencies": {
|
|
51
|
-
"@astrojs/starlight": ">=0.
|
|
53
|
+
"@astrojs/starlight": ">=0.41.0"
|
|
52
54
|
},
|
|
53
55
|
"engines": {
|
|
54
56
|
"node": ">=22.12.0"
|