slidev-theme-tud-db 0.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 +61 -0
- package/assets/ddrg-color.svg +1 -0
- package/assets/ddrg-white.svg +1 -0
- package/assets/fonts/FiraCode-VariableFont_wght.ttf +0 -0
- package/assets/fonts/NotoSans-Italic-VariableFont_wdth,wght.ttf +0 -0
- package/assets/fonts/NotoSans-VariableFont_wdth,wght.ttf +0 -0
- package/assets/fonts/OFL.txt +93 -0
- package/assets/mosaic.svg +1 -0
- package/assets/section-mosaic.svg +1 -0
- package/assets/tud-black.svg +1 -0
- package/assets/tud-white.svg +1 -0
- package/components/DbFrame.vue +24 -0
- package/global-bottom.vue +31 -0
- package/layouts/cover.vue +49 -0
- package/layouts/default.vue +13 -0
- package/layouts/section-num.vue +81 -0
- package/layouts/section.vue +37 -0
- package/layouts/two-cols-grid.vue +41 -0
- package/layouts/two-cols-header.vue +28 -0
- package/markdown.ts +197 -0
- package/package.json +71 -0
- package/setup/shiki.ts +13 -0
- package/styles/db-theme.css +337 -0
- package/styles/fade-transition.css +36 -0
- package/styles/font.css +28 -0
- package/styles/index.ts +6 -0
- package/vite.config.ts +70 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useSlideContext } from '@slidev/client'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
|
|
5
|
+
import mosaic from '../assets/section-mosaic.svg'
|
|
6
|
+
import tudWhite from '../assets/tud-white.svg'
|
|
7
|
+
import ddrgWhite from '../assets/ddrg-white.svg'
|
|
8
|
+
|
|
9
|
+
// The large glyph in the blue area can be overridden per slide via the `num`
|
|
10
|
+
// frontmatter, e.g.
|
|
11
|
+
// layout: section-num
|
|
12
|
+
// num: IV
|
|
13
|
+
// By default it is the slide's position in the table of contents: its 1-based
|
|
14
|
+
// index among its siblings at its own TOC level. Slides missing from the TOC
|
|
15
|
+
// (no title, or `hideInToc`) show no glyph.
|
|
16
|
+
const props = defineProps({
|
|
17
|
+
num: { type: [String, Number], default: '' },
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const { $nav, $page } = useSlideContext()
|
|
21
|
+
|
|
22
|
+
interface TocEntry { no: number, children: TocEntry[] }
|
|
23
|
+
|
|
24
|
+
// 1-based index of the slide numbered `no` among its siblings, or 0 if absent
|
|
25
|
+
function tocIndexOf(items: TocEntry[], no: number): number {
|
|
26
|
+
for (const [i, item] of items.entries()) {
|
|
27
|
+
if (item.no === no)
|
|
28
|
+
return i + 1
|
|
29
|
+
const nested = tocIndexOf(item.children, no)
|
|
30
|
+
if (nested)
|
|
31
|
+
return nested
|
|
32
|
+
}
|
|
33
|
+
return 0
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const num = computed(() => {
|
|
37
|
+
if (String(props.num) !== '')
|
|
38
|
+
return props.num
|
|
39
|
+
return tocIndexOf($nav.value?.tocTree ?? [], $page.value) || ''
|
|
40
|
+
})
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<template>
|
|
44
|
+
<div class="slidev-layout db-cover p-0">
|
|
45
|
+
<div class="relative w-full h-[345px] bg-[#2f57b2]">
|
|
46
|
+
<!-- glyph is painted using bg-clip to circumvent firefox' font scaling limit -->
|
|
47
|
+
<!-- See https://bugzilla.mozilla.org/show_bug.cgi?id=1677551 -->
|
|
48
|
+
<div
|
|
49
|
+
v-if="num !== ''"
|
|
50
|
+
class="absolute font-700 text-[24rem]/[24rem] pt-8 px-4 bg-white bg-clip-text text-transparent"
|
|
51
|
+
>{{ num }}</div>
|
|
52
|
+
<div class="absolute flex gap-[45px] top-[25px] right-[35px]">
|
|
53
|
+
<img
|
|
54
|
+
class="h-[45px]"
|
|
55
|
+
:src="tudWhite"
|
|
56
|
+
alt="TU Dresden"
|
|
57
|
+
/>
|
|
58
|
+
<img
|
|
59
|
+
class="h-[45px]"
|
|
60
|
+
:src="ddrgWhite"
|
|
61
|
+
alt="Dresden Database Research Group"
|
|
62
|
+
/>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<!-- white area: title (#) + subtitle (following text) -->
|
|
67
|
+
<div class="db-cover__title relative m-[30px]">
|
|
68
|
+
<slot />
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</template>
|
|
72
|
+
|
|
73
|
+
<style scoped>
|
|
74
|
+
.db-cover__title :deep(h1) {
|
|
75
|
+
@apply text-4xl! font-700 text-[var(--db-heading-blue)];
|
|
76
|
+
}
|
|
77
|
+
.db-cover__title :deep(p) {
|
|
78
|
+
@apply absolute top-[95px] m-0 text-md;
|
|
79
|
+
color: var(--db-text-soft);
|
|
80
|
+
}
|
|
81
|
+
</style>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// "Titelfolie" — navy band (top, with the DDRG mosaic + both logos) + white
|
|
3
|
+
// title area below. Reconstructed as pure vector: the navy is a CSS fill, the
|
|
4
|
+
// corner mosaic is `cover-mosaic.svg` (rebuilt from the original DB_template.potx
|
|
5
|
+
// title-slide tiles), and the logos are the theme's own white SVGs. No raster.
|
|
6
|
+
// Geometry (positions/sizes in pt == px on the 720-wide canvas) is taken from
|
|
7
|
+
// slideLayout1.xml ("Titelfolie") so it matches the source template.
|
|
8
|
+
import mosaic from '../assets/section-mosaic.svg'
|
|
9
|
+
import tudWhite from '../assets/tud-white.svg'
|
|
10
|
+
import ddrgWhite from '../assets/ddrg-white.svg'
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<div class="slidev-layout db-cover p-0">
|
|
15
|
+
<!-- navy band: solid fill + vector mosaic + logos, all overlaid -->
|
|
16
|
+
<div class="relative w-full mt-[95px] h-[250px] bg-[#97c6ff] overflow-hidden">
|
|
17
|
+
<img class="absolute h-full scale-[1.01]" :src="mosaic" alt="" />
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<!-- white area: title (#) + subtitle (following text) -->
|
|
21
|
+
<div class="db-cover__title relative m-[30px]">
|
|
22
|
+
<slot />
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<style scoped>
|
|
28
|
+
.db-cover__title :deep(h1) {
|
|
29
|
+
@apply text-4xl! font-700 text-[var(--db-heading-blue)];
|
|
30
|
+
}
|
|
31
|
+
.db-cover__title :deep(p) {
|
|
32
|
+
@apply absolute top-[95px] m-0 text-md;
|
|
33
|
+
/* margin: 16px 0 0; */
|
|
34
|
+
/* font-size: 15px; */
|
|
35
|
+
color: var(--db-text-soft);
|
|
36
|
+
}
|
|
37
|
+
</style>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// Like two-cols-header, but the two columns are laid out as a ROW GRID: every
|
|
3
|
+
// top-level <div> in the `::grid::` slot becomes a cell, filling left, right,
|
|
4
|
+
// left, right… Because CSS grid rows align across columns, paired items (e.g. a
|
|
5
|
+
// heading on the left and one on the right) line up automatically and stay
|
|
6
|
+
// aligned when content changes.
|
|
7
|
+
//
|
|
8
|
+
// ---
|
|
9
|
+
// layout: two-cols-grid
|
|
10
|
+
// columns: 55fr 45fr # optional ratio (default 1fr 1fr)
|
|
11
|
+
// ---
|
|
12
|
+
// # Title
|
|
13
|
+
// ::grid::
|
|
14
|
+
// <div> … left of row 1 … </div>
|
|
15
|
+
// <div> … right of row 1 … </div>
|
|
16
|
+
// <div> … left of row 2 … </div>
|
|
17
|
+
// <div> … right of row 2 … </div>
|
|
18
|
+
const props = defineProps({
|
|
19
|
+
columns: { type: String, default: '1fr 1fr' },
|
|
20
|
+
codeSize: { type: String, default: '' },
|
|
21
|
+
})
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<DbFrame :code-size="props.codeSize">
|
|
26
|
+
<div class="db-header"><slot /></div>
|
|
27
|
+
|
|
28
|
+
<div class="db-rowgrid" :style="{ gridTemplateColumns: props.columns }">
|
|
29
|
+
<slot name="grid" />
|
|
30
|
+
</div>
|
|
31
|
+
</DbFrame>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<style scoped>
|
|
35
|
+
.db-rowgrid {
|
|
36
|
+
display: grid;
|
|
37
|
+
column-gap: 28px;
|
|
38
|
+
row-gap: 12px;
|
|
39
|
+
align-items: start; /* each cell starts at the top of its row */
|
|
40
|
+
}
|
|
41
|
+
</style>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// "Zwei Inhalte": full-width title (default slot) + two columns. Chrome via DbFrame.
|
|
3
|
+
// Column ratio is configurable per slide via the `columns` frontmatter, e.g.
|
|
4
|
+
// layout: two-cols-header
|
|
5
|
+
// columns: 3fr 2fr # 60/40 (default is 1fr 1fr = 50/50)
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
columns: { type: String, default: '1fr 1fr' },
|
|
8
|
+
codeSize: { type: String, default: '' },
|
|
9
|
+
})
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<DbFrame :code-size="props.codeSize">
|
|
14
|
+
<div class="db-header"><slot /></div>
|
|
15
|
+
|
|
16
|
+
<div class="db-grid" :style="{ gridTemplateColumns: props.columns }">
|
|
17
|
+
<div class="db-grid__col"><slot name="left" /></div>
|
|
18
|
+
<div class="db-grid__col"><slot name="right" /></div>
|
|
19
|
+
</div>
|
|
20
|
+
</DbFrame>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<style scoped>
|
|
24
|
+
.db-grid {
|
|
25
|
+
display: grid;
|
|
26
|
+
column-gap: 28px;
|
|
27
|
+
}
|
|
28
|
+
</style>
|
package/markdown.ts
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import type { MarkdownExit } from 'markdown-exit'
|
|
2
|
+
import { readFileSync, statSync } from 'node:fs'
|
|
3
|
+
import { parseSync } from '@slidev/parser'
|
|
4
|
+
|
|
5
|
+
// Slidev runs markdown through `markdown-exit` (a markdown-it fork), so the
|
|
6
|
+
// `md` passed to markdownSetup is a MarkdownExit instance, not a markdown-it one.
|
|
7
|
+
|
|
8
|
+
// Custom markdown-it customizations for the theme, wired up from
|
|
9
|
+
// `slidev.markdown.markdownSetup` in vite.config.ts:
|
|
10
|
+
// - footnotes teleported into a <Footnotes> component
|
|
11
|
+
// - LaTeX-style figure captions (`{.caption}`)
|
|
12
|
+
// - LaTeX-style table captions (a `{.caption}` paragraph after a table)
|
|
13
|
+
// - deck-wide "Figure N:" / "Table N:" numbering
|
|
14
|
+
|
|
15
|
+
// --- Global figure/table numbering -----------------------------------------
|
|
16
|
+
// Figures and tables are numbered across the whole deck, not per slide. CSS
|
|
17
|
+
// counters can't do this: Slidev hides off-screen slides with `display: none`,
|
|
18
|
+
// and `counter-increment` is ignored on display:none elements — so a slide
|
|
19
|
+
// never "sees" the figures on other slides. Instead we bake the numbers in at
|
|
20
|
+
// build time. `env.id` (".../deck.md__slidev_<N>.md") tells us which slide is
|
|
21
|
+
// rendering; we parse the full deck once (cached by mtime), count the numbered
|
|
22
|
+
// `{.caption .numbered}` figures/tables on every earlier slide, and use those
|
|
23
|
+
// running totals as the starting offset for this slide.
|
|
24
|
+
|
|
25
|
+
interface DeckCounts { mtimeMs: number, fig: number[], tab: number[] }
|
|
26
|
+
const deckCountsCache = new Map<string, DeckCounts>()
|
|
27
|
+
|
|
28
|
+
// Count `{.caption .numbered}` figures (image) and tables (standalone
|
|
29
|
+
// paragraph) in one slide's raw markdown. Code spans/fences are stripped first
|
|
30
|
+
// so a `{.caption .numbered}` shown as example code is never miscounted.
|
|
31
|
+
function countNumbered(content: string): { fig: number, tab: number } {
|
|
32
|
+
const stripped = content
|
|
33
|
+
.replace(/```[\s\S]*?```/g, '')
|
|
34
|
+
.replace(/`[^`]*`/g, '')
|
|
35
|
+
let fig = 0
|
|
36
|
+
let tab = 0
|
|
37
|
+
const attrBlock = /\{([^{}]*)\}/g
|
|
38
|
+
let match: RegExpExecArray | null
|
|
39
|
+
while ((match = attrBlock.exec(stripped))) {
|
|
40
|
+
const classes = (match[1].match(/\.[\w-]+/g) ?? []).map((c) => c.slice(1))
|
|
41
|
+
if (!classes.includes('numbered')) continue
|
|
42
|
+
if (!classes.includes('table-caption') && !classes.includes('table-footer') && !classes.includes('caption'))
|
|
43
|
+
continue
|
|
44
|
+
if (/!\[[^\]]*\]\([^)]*\)\s*$/.test(stripped.slice(0, match.index)))
|
|
45
|
+
fig++
|
|
46
|
+
else
|
|
47
|
+
tab++
|
|
48
|
+
}
|
|
49
|
+
return { fig, tab }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function deckCounts(entryPath: string): DeckCounts | null {
|
|
53
|
+
let stat
|
|
54
|
+
try {
|
|
55
|
+
stat = statSync(entryPath)
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null
|
|
59
|
+
}
|
|
60
|
+
const cached = deckCountsCache.get(entryPath)
|
|
61
|
+
if (cached && cached.mtimeMs === stat.mtimeMs)
|
|
62
|
+
return cached
|
|
63
|
+
const parsed = parseSync(readFileSync(entryPath, 'utf-8'), entryPath)
|
|
64
|
+
const counts: DeckCounts = { mtimeMs: stat.mtimeMs, fig: [], tab: [] }
|
|
65
|
+
for (const slide of parsed.slides) {
|
|
66
|
+
const { fig, tab } = countNumbered(slide.content ?? '')
|
|
67
|
+
counts.fig.push(fig)
|
|
68
|
+
counts.tab.push(tab)
|
|
69
|
+
}
|
|
70
|
+
deckCountsCache.set(entryPath, counts)
|
|
71
|
+
return counts
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// How many numbered figures/tables appear on slides *before* the one rendering
|
|
75
|
+
// now — i.e. the offset this slide's numbering should start from.
|
|
76
|
+
function numberingOffset(env: any): { fig: number, tab: number } {
|
|
77
|
+
const id = /^(.*)__slidev_(\d+)\.md$/.exec(env?.id ?? '')
|
|
78
|
+
if (!id)
|
|
79
|
+
return { fig: 0, tab: 0 }
|
|
80
|
+
const counts = deckCounts(id[1])
|
|
81
|
+
if (!counts)
|
|
82
|
+
return { fig: 0, tab: 0 }
|
|
83
|
+
const slideIndex = Number(id[2]) - 1
|
|
84
|
+
let fig = 0
|
|
85
|
+
let tab = 0
|
|
86
|
+
for (let i = 0; i < slideIndex && i < counts.fig.length; i++) {
|
|
87
|
+
fig += counts.fig[i]
|
|
88
|
+
tab += counts.tab[i]
|
|
89
|
+
}
|
|
90
|
+
return { fig, tab }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Gather every class on a token, coping with both markdown-it-attrs (one
|
|
94
|
+
// space-separated `class` attr) and Slidev's MDC (a separate `class` attr per
|
|
95
|
+
// class, e.g. [["class","caption"],["class","numbered"]]).
|
|
96
|
+
function classesOf(token: { attrs?: [string, string][] | null }): string[] {
|
|
97
|
+
return (token.attrs ?? [])
|
|
98
|
+
.filter(([name]) => name === 'class')
|
|
99
|
+
.flatMap(([, value]) => value.split(/\s+/))
|
|
100
|
+
.filter(Boolean)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Footnotes: wrap the rendered footnotes block in a <Footnotes> component (see
|
|
104
|
+
// components/Footnotes.vue) instead of emitting it inline in the slide. The
|
|
105
|
+
// component teleports it into global-bottom, so footnotes live in the
|
|
106
|
+
// persistent layer: they don't animate with slide transitions and sit next to
|
|
107
|
+
// the footer. The inline footnote refs ([^1] markers) are produced by other
|
|
108
|
+
// rules and stay in the slide body.
|
|
109
|
+
export function setupFootnotes(md: MarkdownExit): void {
|
|
110
|
+
md.renderer.rules.footnote_block_open = () =>
|
|
111
|
+
'<Footnotes>\n<section class="footnotes">\n<ol class="footnotes-list">\n'
|
|
112
|
+
md.renderer.rules.footnote_block_close = () =>
|
|
113
|
+
'</ol>\n</section>\n</Footnotes>\n'
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// LaTeX-style figure captions: an image marked with `{.caption}` gets its alt
|
|
117
|
+
// text rendered as a caption below it. Add `{.numbered}` too for a "Figure N:"
|
|
118
|
+
// prefix (numbered deck-wide — see numberingOffset). We wrap the <img> and the
|
|
119
|
+
// caption in inline <span>s (not a <figure>) so the markup stays valid inside
|
|
120
|
+
// the <p> that markdown puts a standalone image in. Styling lives in
|
|
121
|
+
// styles/layout.css.
|
|
122
|
+
export function setupFigureCaptions(md: MarkdownExit): void {
|
|
123
|
+
const defaultImage = md.renderer.rules.image
|
|
124
|
+
?? ((tokens, idx, options, env, self) => self.renderToken(tokens, idx, options))
|
|
125
|
+
md.renderer.rules.image = (tokens, idx, options, env, self) => {
|
|
126
|
+
const token = tokens[idx]
|
|
127
|
+
const img = defaultImage(tokens, idx, options, env, self)
|
|
128
|
+
const cls = token.attrGet('class') ?? ''
|
|
129
|
+
if (!/(^|\s)caption(\s|$)/.test(cls))
|
|
130
|
+
return img
|
|
131
|
+
// `token.content` is the image's alt text.
|
|
132
|
+
const alt = md.utils.escapeHtml(token.content ?? '')
|
|
133
|
+
let label = ''
|
|
134
|
+
if (/(^|\s)numbered(\s|$)/.test(cls)) {
|
|
135
|
+
// Continue the figure count from earlier slides; images render in
|
|
136
|
+
// document order, so a per-render running tally stays correct.
|
|
137
|
+
const e = env as { __figureNo?: number }
|
|
138
|
+
e.__figureNo ??= numberingOffset(env).fig
|
|
139
|
+
label = `<span class="caption-label">Figure ${++e.__figureNo}: </span>`
|
|
140
|
+
}
|
|
141
|
+
return `<span class="caption-figure">${img}<span class="caption-text">${label}${alt}</span></span>`
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// LaTeX-style table captions, the table analogue of the figure captions above.
|
|
146
|
+
// A paragraph marked `{.caption}` placed directly after a table becomes that
|
|
147
|
+
// table's caption; add `{.numbered}` for a "Table N:" prefix. We wrap the
|
|
148
|
+
// <table> and the caption in a `display: table` container (.caption-table) —
|
|
149
|
+
// the exact trick the figure captions use — so the caption wraps to the
|
|
150
|
+
// table's width instead of stretching it. Styling lives in styles/layout.css
|
|
151
|
+
// and reuses `.caption-text`.
|
|
152
|
+
export function setupTableCaptions(md: MarkdownExit): void {
|
|
153
|
+
md.core.ruler.push('table_caption', (state) => {
|
|
154
|
+
const tokens = state.tokens
|
|
155
|
+
const Token = state.Token
|
|
156
|
+
|
|
157
|
+
// First pass: find every paragraph that has .table-caption or .table-footer
|
|
158
|
+
const captions: { pInlineIdx: number, numbered: boolean, num?: number }[] = []
|
|
159
|
+
for (let i = 2; i < tokens.length; i++) {
|
|
160
|
+
if (tokens[i].type !== 'paragraph_close') continue
|
|
161
|
+
const pInline = tokens[i - 1]
|
|
162
|
+
const pOpen = tokens[i - 2]
|
|
163
|
+
if (pInline.type !== 'inline' || pOpen.type !== 'paragraph_open') continue
|
|
164
|
+
|
|
165
|
+
const classes = [pOpen, ...(pInline.children ?? [])].flatMap(classesOf)
|
|
166
|
+
if (classes.includes('table-caption') || classes.includes('table-footer')) {
|
|
167
|
+
captions.push({ pInlineIdx: i - 1, numbered: classes.includes('numbered') })
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (captions.length === 0) return
|
|
172
|
+
|
|
173
|
+
let tableNo = numberingOffset(state.env).tab
|
|
174
|
+
for (const caption of captions) {
|
|
175
|
+
if (caption.numbered) caption.num = ++tableNo
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Second pass: inject Tab N:
|
|
179
|
+
for (let k = captions.length - 1; k >= 0; k--) {
|
|
180
|
+
const { pInlineIdx, numbered, num } = captions[k]
|
|
181
|
+
const pInline = tokens[pInlineIdx]
|
|
182
|
+
|
|
183
|
+
// Remove the .numbered marker so it doesn't bleed
|
|
184
|
+
for (const token of [tokens[pInlineIdx - 1], ...(pInline.children ?? [])]) {
|
|
185
|
+
if (token.attrs) {
|
|
186
|
+
token.attrs = token.attrs.filter(([name, value]) => !(name === 'class' && value === 'numbered'))
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (numbered) {
|
|
191
|
+
const label = new Token('html_inline', '', 0)
|
|
192
|
+
label.content = `<span class="caption-label">Tab. ${num}: </span>`
|
|
193
|
+
;(pInline.children ??= []).unshift(label)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
})
|
|
197
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "slidev-theme-tud-db",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Slidev theme cloning the TU Dresden / Dresden Database Research Group PowerPoint template",
|
|
6
|
+
"license": "GPL-3.0-or-later",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"slidev-theme",
|
|
9
|
+
"slidev"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/vmelisch/slidev-theme-db",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/vmelisch/slidev-theme-db.git"
|
|
15
|
+
},
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/vmelisch/slidev-theme-db/issues"
|
|
18
|
+
},
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"assets",
|
|
24
|
+
"components",
|
|
25
|
+
"global-bottom.vue",
|
|
26
|
+
"layouts",
|
|
27
|
+
"markdown.ts",
|
|
28
|
+
"setup",
|
|
29
|
+
"styles",
|
|
30
|
+
"vite.config.ts",
|
|
31
|
+
"README.md"
|
|
32
|
+
],
|
|
33
|
+
"engines": {
|
|
34
|
+
"slidev": ">=0.48.0",
|
|
35
|
+
"node": ">=18.0.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"shiki": "^4.2.0",
|
|
39
|
+
"slidev-addon-gsap": "^0.0.4"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@slidev/cli": "^52.16.0",
|
|
43
|
+
"@slidev/parser": "^52.16.0",
|
|
44
|
+
"vue": "^3.5.38"
|
|
45
|
+
},
|
|
46
|
+
"slidev": {
|
|
47
|
+
"defaults": {
|
|
48
|
+
"addons": [
|
|
49
|
+
"gsap"
|
|
50
|
+
],
|
|
51
|
+
"aspectRatio": "16/9",
|
|
52
|
+
"canvasWidth": 960,
|
|
53
|
+
"colorSchema": "light",
|
|
54
|
+
"mdc": true,
|
|
55
|
+
"comark": true,
|
|
56
|
+
"presenter": true,
|
|
57
|
+
"titleTemplate": "%s",
|
|
58
|
+
"transition": "slide-fade-left | slide-fade-right",
|
|
59
|
+
"fonts": {
|
|
60
|
+
"sans": "NotoSans",
|
|
61
|
+
"mono": "FiraCode",
|
|
62
|
+
"provider": null
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"scripts": {
|
|
67
|
+
"build": "slidev build examples/slides.md",
|
|
68
|
+
"dev": "slidev examples/slides.md --remote",
|
|
69
|
+
"export": "slidev export examples/slides.md"
|
|
70
|
+
}
|
|
71
|
+
}
|
package/setup/shiki.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Default Shiki color scheme for fenced code blocks (```lang ... ```).
|
|
2
|
+
// Matches the inline-code highlighter (see vite.config.ts) and the chair's
|
|
3
|
+
// template, so inline and fenced code look the same.
|
|
4
|
+
//
|
|
5
|
+
// NB: no `import { defineShikiSetup } from '@slidev/types'` on purpose — it's
|
|
6
|
+
// only a type helper, and Slidev calls this default export as `setup(ctx)`.
|
|
7
|
+
// catppuccin-latte / -mocha are bundled with Shiki, so no extra deps needed.
|
|
8
|
+
export default () => ({
|
|
9
|
+
themes: {
|
|
10
|
+
light: 'catppuccin-latte',
|
|
11
|
+
dark: 'catppuccin-mocha',
|
|
12
|
+
},
|
|
13
|
+
})
|