presentation-sanity 0.4.3__py3-none-any.whl
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.
- presentation_sanity/__init__.py +3 -0
- presentation_sanity/__main__.py +7 -0
- presentation_sanity/assets/vitepress/citations.css +89 -0
- presentation_sanity/assets/vitepress/config.mts +277 -0
- presentation_sanity/assets/vitepress/theme.ts +43 -0
- presentation_sanity/bibliography.py +616 -0
- presentation_sanity/build.py +392 -0
- presentation_sanity/check.py +205 -0
- presentation_sanity/cli.py +837 -0
- presentation_sanity/figures_render.py +175 -0
- presentation_sanity/init_project.py +563 -0
- presentation_sanity/manifest.py +581 -0
- presentation_sanity/manim_render.py +250 -0
- presentation_sanity/markdown_meta.py +114 -0
- presentation_sanity/pages.py +257 -0
- presentation_sanity/pptx_export.py +307 -0
- presentation_sanity/project.py +472 -0
- presentation_sanity/skills/presentation-sanity/SKILL.md +100 -0
- presentation_sanity/skills/presentation-sanity/modes/_shared.md +86 -0
- presentation_sanity/skills/presentation-sanity/modes/blog.md +44 -0
- presentation_sanity/skills/presentation-sanity/modes/check.md +45 -0
- presentation_sanity/skills/presentation-sanity/modes/cite.md +41 -0
- presentation_sanity/skills/presentation-sanity/modes/deploy.md +54 -0
- presentation_sanity/skills/presentation-sanity/modes/export.md +21 -0
- presentation_sanity/skills/presentation-sanity/modes/figures.md +32 -0
- presentation_sanity/skills/presentation-sanity/modes/init.md +75 -0
- presentation_sanity/skills/presentation-sanity/modes/manim.md +40 -0
- presentation_sanity/skills/presentation-sanity/modes/new.md +45 -0
- presentation_sanity/skills/presentation-sanity/modes/slides.md +49 -0
- presentation_sanity/skills/presentation-sanity/modes/variables.md +43 -0
- presentation_sanity/slidev.py +153 -0
- presentation_sanity/template/.gitignore +21 -0
- presentation_sanity/template/LICENSE +21 -0
- presentation_sanity/template/README.md +376 -0
- presentation_sanity/template/manifest.yaml +126 -0
- presentation_sanity/template/package-lock.json +11788 -0
- presentation_sanity/template/package.json +30 -0
- presentation_sanity/template/presentations/example/blog.md +112 -0
- presentation_sanity/template/presentations/example/manifest.yaml +10 -0
- presentation_sanity/template/presentations/example/slides.md +181 -0
- presentation_sanity/template/presentations/example/style.css +144 -0
- presentation_sanity/template/public/manim/one_source.poster.png +0 -0
- presentation_sanity/template/public/manim/one_source.webm +0 -0
- presentation_sanity/template/pyproject.toml +32 -0
- presentation_sanity/template/refs.bib +36 -0
- presentation_sanity/template/scenes/one_source.py +109 -0
- presentation_sanity/template/shared/blog.css +19 -0
- presentation_sanity/template/shared/components/DataValue.vue +93 -0
- presentation_sanity/template/shared/components/FigureImage.vue +57 -0
- presentation_sanity/template/shared/components/ManimFigure.vue +124 -0
- presentation_sanity/template/shared/components/ProvenancePanel.vue +344 -0
- presentation_sanity/template/shared/composables/useProvenance.ts +19 -0
- presentation_sanity/template/shared/global-bottom.vue +12 -0
- presentation_sanity/template/shared/layouts/manim.vue +206 -0
- presentation_sanity/template/shared/package.json +7 -0
- presentation_sanity/template/shared/style.css +40 -0
- presentation_sanity/template/shared/vite.config.ts +42 -0
- presentation_sanity/template-source.json +4 -0
- presentation_sanity/vitepress.py +467 -0
- presentation_sanity-0.4.3.dist-info/METADATA +509 -0
- presentation_sanity-0.4.3.dist-info/RECORD +64 -0
- presentation_sanity-0.4.3.dist-info/WHEEL +4 -0
- presentation_sanity-0.4.3.dist-info/entry_points.txt +2 -0
- presentation_sanity-0.4.3.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/* GENERATED by presentation-sanity — DO NOT EDIT.
|
|
2
|
+
* Defaults for \cite{} labels and the \bibliography list. The subject's own
|
|
3
|
+
* blog.css is imported after this file, so anything here can be overridden
|
|
4
|
+
* there without touching the generated theme. */
|
|
5
|
+
|
|
6
|
+
/* ─── In-text citations ──────────────────────────────────────────────────── */
|
|
7
|
+
.vp-doc a.cite {
|
|
8
|
+
font-variant-numeric: tabular-nums;
|
|
9
|
+
text-decoration: none;
|
|
10
|
+
font-weight: 500;
|
|
11
|
+
white-space: nowrap;
|
|
12
|
+
}
|
|
13
|
+
.vp-doc a.cite:hover {
|
|
14
|
+
text-decoration: underline;
|
|
15
|
+
}
|
|
16
|
+
.cite--missing {
|
|
17
|
+
background: rgba(255, 80, 80, 0.16);
|
|
18
|
+
color: #c00;
|
|
19
|
+
border-radius: 2px;
|
|
20
|
+
padding: 0 0.2em;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* ─── Reference list ─────────────────────────────────────────────────────── */
|
|
24
|
+
.psanity-bibliography {
|
|
25
|
+
margin-top: 3.5rem;
|
|
26
|
+
border-top: 1px solid var(--vp-c-divider);
|
|
27
|
+
padding-top: 0.5rem;
|
|
28
|
+
}
|
|
29
|
+
.vp-doc .psanity-bibliography > :first-child {
|
|
30
|
+
margin-top: 1.2rem;
|
|
31
|
+
}
|
|
32
|
+
.vp-doc .psanity-bibliography ol.ref-list {
|
|
33
|
+
list-style: none;
|
|
34
|
+
padding-left: 0;
|
|
35
|
+
margin: 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/*
|
|
39
|
+
* Hanging indent: the marker sits in the gutter and continuation lines align
|
|
40
|
+
* under the reference text rather than under the marker.
|
|
41
|
+
*
|
|
42
|
+
* Inline flow is deliberate. `display: flex` or `grid` here looks like the
|
|
43
|
+
* obvious way to put the number beside the text, but a reference is a run of
|
|
44
|
+
* inline nodes — <em>journal</em>, <strong>volume</strong>, bare text, the DOI
|
|
45
|
+
* link — and each one becomes its own flex/grid item, so the entry renders as
|
|
46
|
+
* a row of narrow columns instead of a paragraph.
|
|
47
|
+
*/
|
|
48
|
+
.vp-doc .psanity-bibliography li.ref {
|
|
49
|
+
padding-left: 2.5em;
|
|
50
|
+
text-indent: -2.5em;
|
|
51
|
+
margin: 0.55rem 0;
|
|
52
|
+
font-size: 0.9em;
|
|
53
|
+
line-height: 1.65;
|
|
54
|
+
color: var(--vp-c-text-2);
|
|
55
|
+
/* Jumping from a citation shouldn't tuck the entry under the navbar. */
|
|
56
|
+
scroll-margin-top: 6rem;
|
|
57
|
+
}
|
|
58
|
+
.vp-doc .psanity-bibliography li.ref + li.ref {
|
|
59
|
+
margin-top: 0.55rem;
|
|
60
|
+
}
|
|
61
|
+
.vp-doc .psanity-bibliography .ref-number {
|
|
62
|
+
display: inline-block;
|
|
63
|
+
width: 2.5em; /* matches the indent, so text lines up exactly */
|
|
64
|
+
text-indent: 0; /* don't inherit the negative indent */
|
|
65
|
+
font-variant-numeric: tabular-nums;
|
|
66
|
+
color: var(--vp-c-text-3);
|
|
67
|
+
}
|
|
68
|
+
.vp-doc .psanity-bibliography li.ref em {
|
|
69
|
+
color: var(--vp-c-text-1);
|
|
70
|
+
}
|
|
71
|
+
.vp-doc .psanity-bibliography a {
|
|
72
|
+
text-decoration: none;
|
|
73
|
+
font-weight: 400;
|
|
74
|
+
}
|
|
75
|
+
.vp-doc .psanity-bibliography a:hover {
|
|
76
|
+
text-decoration: underline;
|
|
77
|
+
}
|
|
78
|
+
.vp-doc .psanity-bibliography a.ref-link {
|
|
79
|
+
font-size: 0.95em;
|
|
80
|
+
overflow-wrap: anywhere; /* long DOIs shouldn't overflow the column */
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* Briefly mark the entry you jumped to. box-shadow rather than padding, so
|
|
84
|
+
* highlighting doesn't shift the hanging indent. */
|
|
85
|
+
.vp-doc .psanity-bibliography li.ref:target {
|
|
86
|
+
background: var(--vp-c-default-soft);
|
|
87
|
+
border-radius: 3px;
|
|
88
|
+
box-shadow: 0 0 0 0.45rem var(--vp-c-default-soft);
|
|
89
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
// GENERATED by presentation-sanity — DO NOT EDIT.
|
|
2
|
+
// Rewritten from manifest.yaml on every `presentation-sanity build|dev`.
|
|
3
|
+
// Change the blog's title, nav, base, math macros, etc. under `outputs.<key>:`
|
|
4
|
+
// and `math:` in manifest.yaml. To take full ownership instead, copy this
|
|
5
|
+
// directory elsewhere and point VitePress at it yourself.
|
|
6
|
+
import { defineConfig } from 'vitepress'
|
|
7
|
+
__IMPORTS__
|
|
8
|
+
|
|
9
|
+
// Everything derivable from manifest.yaml, injected as a literal.
|
|
10
|
+
const generated = __CONFIG_JSON__
|
|
11
|
+
|
|
12
|
+
// ─── Bare LaTeX display environments ────────────────────────────────────────
|
|
13
|
+
// markdown-it-mathjax3 only recognises `$…$` and `$$…$$`, so a document that
|
|
14
|
+
// opens with `\begin{align}` — the way you'd write it in a .tex file — falls
|
|
15
|
+
// through to the paragraph rule and renders as literal text with its `\\`
|
|
16
|
+
// row breaks eaten. This block rule consumes `\begin{env}…\end{env}` and emits
|
|
17
|
+
// the plugin's own `math_block` token, so such a block renders exactly as if
|
|
18
|
+
// it had been fenced in `$$`.
|
|
19
|
+
//
|
|
20
|
+
// Names from `math.environments` in the manifest; starred variants match too.
|
|
21
|
+
// In-math-only environments (matrix, cases, …) are deliberately absent — at
|
|
22
|
+
// block level they would swallow the prose that follows.
|
|
23
|
+
const MATH_ENVIRONMENTS: string[] = __ENVIRONMENTS__
|
|
24
|
+
|
|
25
|
+
function installLatexEnvironments(md: any) {
|
|
26
|
+
if (!MATH_ENVIRONMENTS.length) return
|
|
27
|
+
const allowed = new Set(MATH_ENVIRONMENTS.map((e) => e.replace(/\*$/, '')))
|
|
28
|
+
|
|
29
|
+
function latexEnvironment(state: any, startLine: number, endLine: number, silent: boolean) {
|
|
30
|
+
// Four-space indent is a code block, not math.
|
|
31
|
+
if (state.sCount[startLine] - state.blkIndent >= 4) return false
|
|
32
|
+
|
|
33
|
+
const begin = state.bMarks[startLine] + state.tShift[startLine]
|
|
34
|
+
const opening = /^\\begin\{([A-Za-z]+\*?)\}/.exec(
|
|
35
|
+
state.src.slice(begin, state.eMarks[startLine]),
|
|
36
|
+
)
|
|
37
|
+
if (!opening) return false
|
|
38
|
+
|
|
39
|
+
const env = opening[1]
|
|
40
|
+
if (!allowed.has(env.replace(/\*$/, ''))) return false
|
|
41
|
+
if (silent) return true
|
|
42
|
+
|
|
43
|
+
// Scan for the matching \end{env}, counting nested opens of the same
|
|
44
|
+
// environment so `align` inside `align` closes at the right line.
|
|
45
|
+
const closing = `\\end{${env}}`
|
|
46
|
+
const reopen = `\\begin{${env}}`
|
|
47
|
+
let depth = 0
|
|
48
|
+
let line = startLine
|
|
49
|
+
let found = false
|
|
50
|
+
for (; line < endLine; line++) {
|
|
51
|
+
const text = state.src.slice(
|
|
52
|
+
state.bMarks[line] + state.tShift[line],
|
|
53
|
+
state.eMarks[line],
|
|
54
|
+
)
|
|
55
|
+
if (line > startLine && text.includes(reopen)) depth++
|
|
56
|
+
if (text.includes(closing)) {
|
|
57
|
+
if (depth === 0) { found = true; break }
|
|
58
|
+
depth--
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Unterminated: leave it to the paragraph rule rather than eating the
|
|
62
|
+
// rest of the document.
|
|
63
|
+
if (!found) return false
|
|
64
|
+
|
|
65
|
+
const token = state.push('math_block', 'math', 0)
|
|
66
|
+
token.block = true
|
|
67
|
+
token.content = state.getLines(startLine, line + 1, state.blkIndent, false)
|
|
68
|
+
token.map = [startLine, line + 1]
|
|
69
|
+
token.markup = '$$'
|
|
70
|
+
state.line = line + 1
|
|
71
|
+
return true
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
md.block.ruler.before('paragraph', 'latex_environment', latexEnvironment, {
|
|
75
|
+
alt: ['paragraph', 'reference', 'blockquote', 'list'],
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ─── Citations ──────────────────────────────────────────────────────────────
|
|
80
|
+
// LaTeX-shaped citing without a LaTeX toolchain. `\cite{key}` becomes a linked
|
|
81
|
+
// label, `\bibliography` renders the reference list. Entries were parsed and
|
|
82
|
+
// formatted from the manifest's .bib sources at scaffold time and injected
|
|
83
|
+
// below, so nothing about citations reaches the browser — the page ships plain
|
|
84
|
+
// anchors, the same bargain the math rendering makes.
|
|
85
|
+
type BibEntry = {
|
|
86
|
+
html: string
|
|
87
|
+
text: string
|
|
88
|
+
label: string
|
|
89
|
+
labelBare: string
|
|
90
|
+
year: string
|
|
91
|
+
sortAuthor: string
|
|
92
|
+
sortYear: string
|
|
93
|
+
}
|
|
94
|
+
type BibData = {
|
|
95
|
+
style: 'numeric' | 'author-year'
|
|
96
|
+
sort: 'appearance' | 'author' | 'year'
|
|
97
|
+
title: string
|
|
98
|
+
heading: string
|
|
99
|
+
entries: Record<string, BibEntry>
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const BIBLIOGRAPHY: BibData | null = __BIBLIOGRAPHY__
|
|
103
|
+
|
|
104
|
+
const CITE_COMMANDS: Record<string, 'p' | 't' | 'no'> = {
|
|
105
|
+
cite: 'p',
|
|
106
|
+
citep: 'p',
|
|
107
|
+
citealp: 'p',
|
|
108
|
+
citet: 't',
|
|
109
|
+
citealt: 't',
|
|
110
|
+
nocite: 'no',
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function escapeHtml(s: string) {
|
|
114
|
+
return s.replace(/&/g, '&').replace(/</g, '<')
|
|
115
|
+
.replace(/>/g, '>').replace(/"/g, '"')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function installCitations(md: any) {
|
|
119
|
+
const bib = BIBLIOGRAPHY
|
|
120
|
+
if (!bib) return
|
|
121
|
+
const warned = new Set<string>()
|
|
122
|
+
|
|
123
|
+
// \cite{a,b} · \citep[p. 12]{a} · \citet{a} · \nocite{a}
|
|
124
|
+
function citeRule(state: any, silent: boolean) {
|
|
125
|
+
if (state.src.charCodeAt(state.pos) !== 0x5c /* \ */) return false
|
|
126
|
+
const re = /^\\(cite|citep|citealp|citet|citealt|nocite)\s*(?:\[([^\]]*)\])?\{([^}]*)\}/
|
|
127
|
+
const m = re.exec(state.src.slice(state.pos))
|
|
128
|
+
if (!m) return false
|
|
129
|
+
const keys = m[3].split(',').map((k: string) => k.trim()).filter(Boolean)
|
|
130
|
+
if (!keys.length) return false
|
|
131
|
+
if (!silent) {
|
|
132
|
+
const token = state.push('psanity_cite', '', 0)
|
|
133
|
+
token.meta = { keys, form: CITE_COMMANDS[m[1]], note: (m[2] || '').trim() }
|
|
134
|
+
}
|
|
135
|
+
state.pos += m[0].length
|
|
136
|
+
return true
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// A line that is only \bibliography, \bibliography{...} or \printbibliography
|
|
140
|
+
function bibliographyRule(state: any, startLine: number, _end: number, silent: boolean) {
|
|
141
|
+
if (state.sCount[startLine] - state.blkIndent >= 4) return false
|
|
142
|
+
const line = state.src
|
|
143
|
+
.slice(state.bMarks[startLine] + state.tShift[startLine], state.eMarks[startLine])
|
|
144
|
+
.trim()
|
|
145
|
+
if (!/^\\(?:print)?bibliography(\{[^}]*\})?$/.test(line)) return false
|
|
146
|
+
if (silent) return true
|
|
147
|
+
state.push('psanity_bibliography', '', 0)
|
|
148
|
+
state.line = startLine + 1
|
|
149
|
+
return true
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Collect every cited key, then fix numbering and list order in one pass —
|
|
153
|
+
// so `sort: author` can still produce ascending [1],[2] in the text.
|
|
154
|
+
function collectRule(state: any) {
|
|
155
|
+
const seen: string[] = []
|
|
156
|
+
const visit = (tokens: any[]) => {
|
|
157
|
+
for (const t of tokens) {
|
|
158
|
+
if (t.type === 'psanity_cite') {
|
|
159
|
+
for (const k of t.meta.keys) if (!seen.includes(k)) seen.push(k)
|
|
160
|
+
}
|
|
161
|
+
if (t.children) visit(t.children)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
visit(state.tokens)
|
|
165
|
+
|
|
166
|
+
const known = seen.filter((k) => bib.entries[k])
|
|
167
|
+
let order = known.slice()
|
|
168
|
+
if (bib.sort === 'author') {
|
|
169
|
+
order.sort((a, b) => bib.entries[a].sortAuthor.localeCompare(bib.entries[b].sortAuthor))
|
|
170
|
+
} else if (bib.sort === 'year') {
|
|
171
|
+
order.sort((a, b) => bib.entries[a].sortYear.localeCompare(bib.entries[b].sortYear))
|
|
172
|
+
}
|
|
173
|
+
const numbers: Record<string, number> = {}
|
|
174
|
+
order.forEach((k, i) => { numbers[k] = i + 1 })
|
|
175
|
+
state.env.psanityBib = { order, numbers }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// BEFORE 'escape', not after: markdown-it's escape rule consumes `\X` for
|
|
179
|
+
// *any* X — including `\c` — and returns true, so a rule registered after it
|
|
180
|
+
// never sees `\cite`. Running first is safe because this rule only matches a
|
|
181
|
+
// backslash followed by a known citation command, so `\$100` and friends
|
|
182
|
+
// still fall through to escape, and `$…$` still reaches the math rule.
|
|
183
|
+
md.inline.ruler.before('escape', 'psanity_cite', citeRule)
|
|
184
|
+
md.block.ruler.before('paragraph', 'psanity_bibliography', bibliographyRule, {
|
|
185
|
+
alt: ['paragraph', 'reference', 'blockquote', 'list'],
|
|
186
|
+
})
|
|
187
|
+
md.core.ruler.push('psanity_collect_citations', collectRule)
|
|
188
|
+
|
|
189
|
+
md.renderer.rules.psanity_cite = (tokens: any[], idx: number, _o: any, env: any) => {
|
|
190
|
+
const { keys, form, note } = tokens[idx].meta
|
|
191
|
+
if (form === 'no') return ''
|
|
192
|
+
const numbers = env?.psanityBib?.numbers ?? {}
|
|
193
|
+
const parts = keys.map((key: string) => {
|
|
194
|
+
const entry = bib.entries[key]
|
|
195
|
+
if (!entry) {
|
|
196
|
+
if (!warned.has(key)) {
|
|
197
|
+
warned.add(key)
|
|
198
|
+
console.warn(` warning: \\cite{${key}} — no such key in the bibliography`)
|
|
199
|
+
}
|
|
200
|
+
return `<span class="cite cite--missing">?${escapeHtml(key)}</span>`
|
|
201
|
+
}
|
|
202
|
+
const href = `#ref-${encodeURIComponent(key)}`
|
|
203
|
+
const title = escapeHtml(entry.text)
|
|
204
|
+
if (bib.style === 'author-year') {
|
|
205
|
+
const inner = form === 't' ? entry.labelBare : entry.label
|
|
206
|
+
return `<a class="cite" href="${href}" title="${title}">${escapeHtml(inner)}</a>`
|
|
207
|
+
}
|
|
208
|
+
return `<a class="cite" href="${href}" title="${title}">${numbers[key]}</a>`
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
// The locator comes straight from markdown, so apply the TeX dash
|
|
212
|
+
// conventions the .bib fields already get: `pp. 401--403` → 401–403.
|
|
213
|
+
const suffix = note
|
|
214
|
+
? `, ${escapeHtml(note.replace(/---/g, '\u2014').replace(/--/g, '\u2013'))}`
|
|
215
|
+
: ''
|
|
216
|
+
if (bib.style === 'author-year') {
|
|
217
|
+
// \citet{k} → Smith et al. (2020); \citep{k} → (Smith et al., 2020)
|
|
218
|
+
if (form === 't' && keys.length === 1) {
|
|
219
|
+
const entry = bib.entries[keys[0]]
|
|
220
|
+
const year = entry ? escapeHtml(entry.year) : ''
|
|
221
|
+
return `${parts[0]} (${year}${suffix})`
|
|
222
|
+
}
|
|
223
|
+
return `(${parts.join('; ')}${suffix})`
|
|
224
|
+
}
|
|
225
|
+
const body = `[${parts.join(', ')}${suffix}]`
|
|
226
|
+
return form === 't' && keys.length === 1 && bib.entries[keys[0]]
|
|
227
|
+
? `${escapeHtml(bib.entries[keys[0]].labelBare)} ${body}`
|
|
228
|
+
: body
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
md.renderer.rules.psanity_bibliography = (_t: any, _i: number, _o: any, env: any) => {
|
|
232
|
+
const order: string[] = env?.psanityBib?.order ?? []
|
|
233
|
+
if (!order.length) return ''
|
|
234
|
+
const numbered = bib.style === 'numeric'
|
|
235
|
+
const items = order
|
|
236
|
+
.map((key) => {
|
|
237
|
+
const e = bib.entries[key]
|
|
238
|
+
// No trailing space: .ref-number is a fixed-width inline-block, so the
|
|
239
|
+
// gap is the CSS width and the text aligns with the hanging indent.
|
|
240
|
+
const n = numbered ? `<span class="ref-number">[${env.psanityBib.numbers[key]}]</span>` : ''
|
|
241
|
+
return `<li class="ref" id="ref-${encodeURIComponent(key)}">${n}${e.html}</li>`
|
|
242
|
+
})
|
|
243
|
+
.join('\n')
|
|
244
|
+
const h = /^h[1-6]$/.test(bib.heading) ? bib.heading : 'h2'
|
|
245
|
+
const title = bib.title
|
|
246
|
+
? `<${h} id="${encodeURIComponent(bib.title.toLowerCase().replace(/\s+/g, '-'))}">${escapeHtml(bib.title)}</${h}>`
|
|
247
|
+
: ''
|
|
248
|
+
return `<div class="psanity-bibliography">\n${title}\n<ol class="ref-list">\n${items}\n</ol>\n</div>\n`
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export default defineConfig({
|
|
253
|
+
...generated,
|
|
254
|
+
markdown: {
|
|
255
|
+
...generated.markdown,
|
|
256
|
+
// Runs after VitePress installs markdown-it-mathjax3, so the `math_block`
|
|
257
|
+
// renderer the rule above emits into is already registered.
|
|
258
|
+
config: (md: any) => {
|
|
259
|
+
if (generated.markdown?.math) installLatexEnvironments(md)
|
|
260
|
+
installCitations(md)
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
vite: {
|
|
264
|
+
// `configFile: false` is load-bearing. The subject root also holds a
|
|
265
|
+
// `vite.config.ts` for Slidev, and VitePress would otherwise merge it in:
|
|
266
|
+
// its `base: './'` is invalid for VitePress routing, and its yaml plugin
|
|
267
|
+
// would transform manifest.yaml a SECOND time — the double pass re-parses
|
|
268
|
+
// the emitted JS as YAML and hands every component a string instead of the
|
|
269
|
+
// manifest. This config is self-contained; the deck's is left to the deck.
|
|
270
|
+
configFile: false,
|
|
271
|
+
plugins: [__VITE_PLUGINS__],
|
|
272
|
+
// In a project: `@project` → the project root, `@shared` → its shared/ Vue
|
|
273
|
+
// layer — the same aliases the deck's shared/vite.config.ts defines, so a
|
|
274
|
+
// component imports `@project/manifest.yaml` identically in both engines.
|
|
275
|
+
resolve: { alias: __ALIASES__ },
|
|
276
|
+
},
|
|
277
|
+
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// GENERATED by presentation-sanity — DO NOT EDIT.
|
|
2
|
+
//
|
|
3
|
+
// Wires the shared Vue components into VitePress so the same components back
|
|
4
|
+
// both the Slidev deck and the blog:
|
|
5
|
+
//
|
|
6
|
+
// * every component file is registered globally under its filename, which
|
|
7
|
+
// is exactly the convention Slidev already uses. `<DataValue>`,
|
|
8
|
+
// `<ManimFigure>`, `<FigureImage>` therefore work in markdown with no
|
|
9
|
+
// per-file imports.
|
|
10
|
+
// * `ProvenancePanel` is mounted once in the `layout-bottom` slot — the
|
|
11
|
+
// VitePress equivalent of Slidev's `global-bottom.vue` singleton.
|
|
12
|
+
import { h } from 'vue'
|
|
13
|
+
import type { Theme } from 'vitepress'
|
|
14
|
+
import DefaultTheme from 'vitepress/theme'
|
|
15
|
+
__THEME_IMPORTS__
|
|
16
|
+
|
|
17
|
+
// Literal globs (Vite requires static patterns), in priority order: a later
|
|
18
|
+
// directory overrides an earlier one, so a presentation's own component wins
|
|
19
|
+
// over the shared component of the same name.
|
|
20
|
+
const sources = [
|
|
21
|
+
__COMPONENT_GLOBS__
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
const components: Record<string, any> = {}
|
|
25
|
+
for (const modules of sources) {
|
|
26
|
+
for (const [path, mod] of Object.entries(modules)) {
|
|
27
|
+
const name = path.split('/').pop()!.replace(/\.vue$/, '')
|
|
28
|
+
if (mod?.default) components[name] = mod.default
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default {
|
|
33
|
+
extends: DefaultTheme,
|
|
34
|
+
Layout() {
|
|
35
|
+
const panel = components.ProvenancePanel
|
|
36
|
+
return h(DefaultTheme.Layout, null, panel ? { 'layout-bottom': () => h(panel) } : {})
|
|
37
|
+
},
|
|
38
|
+
enhanceApp({ app }) {
|
|
39
|
+
for (const [name, component] of Object.entries(components)) {
|
|
40
|
+
app.component(name, component)
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
} satisfies Theme
|