slidev-theme-tud-db 0.0.8 → 0.0.9
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 +6 -4
- package/package.json +1 -1
- package/setup/shiki.ts +80 -134
- package/styles/db-theme.css +8 -0
- package/vite.config.ts +45 -42
package/README.md
CHANGED
|
@@ -53,9 +53,11 @@ cp -r node_modules/slidev-theme-db/skills/slidev-theme-db .claude/skills/
|
|
|
53
53
|
- `components/DbFrame.vue` — shared chrome (logo, footer, padding).
|
|
54
54
|
- `layouts/` — `cover`, `default`, `two-cols-header`, `two-cols-grid` (wrap `DbFrame`).
|
|
55
55
|
- `styles/db-theme.css` — tokens, typography, lists, tables (keyed on `.db-default`).
|
|
56
|
-
- `setup/shiki.ts`
|
|
57
|
-
the
|
|
58
|
-
`
|
|
59
|
-
(
|
|
56
|
+
- `setup/shiki.ts` — code highlighting: the SQL grammar (Shiki's, plus a few
|
|
57
|
+
rules) and the colour themes (catppuccin, upright, with SQL tweaks), built by
|
|
58
|
+
`highlighting()`. Fenced code uses them through Slidev; `vite.config.ts` uses
|
|
59
|
+
the same `highlighting()` for inline `{lang}` code, so the two never drift.
|
|
60
|
+
It also holds the transformer behind `⟦name:text⟧` code marks (spans a
|
|
61
|
+
slide's timeline can recolour).
|
|
60
62
|
- Link the theme with `link:` (not `file:`), and **restart** the dev server after
|
|
61
63
|
editing theme files (Vite doesn't hot-reload `node_modules`).
|
package/package.json
CHANGED
package/setup/shiki.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// Shiki setup for fenced code blocks (```lang ... ```) and magic-move blocks.
|
|
2
|
+
// vite.config.ts highlights inline code with the same grammar and colours (see
|
|
3
|
+
// `highlighting()` below), so inline and fenced code look the same.
|
|
4
4
|
//
|
|
5
5
|
// NB: no `import { defineShikiSetup } from '@slidev/types'` on purpose — it's
|
|
6
6
|
// only a type helper, and Slidev calls this default export as `setup(ctx)`.
|
|
7
|
-
//
|
|
7
|
+
// The grammar and the colour themes are Shiki's own bundled ones, adjusted
|
|
8
|
+
// below, so no extra deps are needed.
|
|
9
|
+
import { bundledLanguages, bundledThemes } from 'shiki'
|
|
8
10
|
|
|
9
11
|
// Code marks: wrap exact character ranges of a fenced block so a slide can
|
|
10
12
|
// recolour that text, e.g. at a click. Inside the code, write ⟦name:text⟧: the
|
|
@@ -46,138 +48,82 @@ export const codeMarks = {
|
|
|
46
48
|
},
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
function
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const leading = token.content.slice(0, token.content.indexOf(trimmed))
|
|
75
|
-
const trailing = token.content.slice(token.content.indexOf(trimmed) + trimmed.length)
|
|
76
|
-
if (leading) newTokens.push({ ...token, content: leading, fontStyle: 0, htmlStyle: { color: '#4C4F69', '--shiki-light': '#4C4F69', '--shiki-dark': '#CDD6F4' } })
|
|
77
|
-
newTokens.push({
|
|
78
|
-
...token,
|
|
79
|
-
content: trimmed,
|
|
80
|
-
color: '#059669',
|
|
81
|
-
fontStyle: 0,
|
|
82
|
-
htmlStyle: { color: '#059669', '--shiki-light': '#059669', '--shiki-dark': '#34D399' },
|
|
83
|
-
})
|
|
84
|
-
if (trailing) newTokens.push({ ...token, content: trailing, fontStyle: 0, htmlStyle: { color: '#4C4F69', '--shiki-light': '#4C4F69', '--shiki-dark': '#CDD6F4' } })
|
|
85
|
-
continue
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// 1. Intercept NULL if Shiki tagged it as keyword purple (including combined tokens like "IS NOT NULL")
|
|
89
|
-
if (/\bNULL\b/i.test(token.content)) {
|
|
90
|
-
const s = getSqlStyle('NULL')
|
|
91
|
-
const parts = token.content.split(/\b(NULL)\b/i)
|
|
92
|
-
let currOffset = token.offset
|
|
93
|
-
for (const part of parts) {
|
|
94
|
-
if (!part) continue
|
|
95
|
-
if (/^NULL$/i.test(part)) {
|
|
96
|
-
newTokens.push({
|
|
97
|
-
...token,
|
|
98
|
-
content: part,
|
|
99
|
-
offset: currOffset,
|
|
100
|
-
color: s.color,
|
|
101
|
-
fontStyle: 0,
|
|
102
|
-
htmlStyle: { color: s.color, '--shiki-light': s.light, '--shiki-dark': s.dark },
|
|
103
|
-
})
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
newTokens.push({ ...token, content: part, offset: currOffset })
|
|
107
|
-
}
|
|
108
|
-
currOffset += part.length
|
|
109
|
-
}
|
|
110
|
-
continue
|
|
111
|
-
}
|
|
51
|
+
// SQL: Shiki's grammar, with a few rules added. Being grammar rules, they
|
|
52
|
+
// apply only where SQL code is parsed, never inside comments or strings.
|
|
53
|
+
// - NULL, TRUE, FALSE and UNKNOWN are constants (the grammar knows only NULL,
|
|
54
|
+
// as a keyword, and reads `IS NOT NULL` as a single keyword: here the
|
|
55
|
+
// keyword part and the constant are told apart).
|
|
56
|
+
// - EXPLAIN and ANALYZE are keywords.
|
|
57
|
+
// - A name right before `(` is a function call: `ceil(x)`, `jsonb_agg(…)`;
|
|
58
|
+
// so are common function names on their own, as in an inline `AVG` (the
|
|
59
|
+
// grammar knows functions only before `(`).
|
|
60
|
+
// These rules come last, so the grammar's own reading of a word, such as
|
|
61
|
+
// the keyword in `IN (` or the type in `DECIMAL(10, 2)`, takes precedence.
|
|
62
|
+
const CONSTANTS = 'NULL|TRUE|FALSE|UNKNOWN'
|
|
63
|
+
const FUNCTIONS = 'COUNT|AVG|SUM|MIN|MAX|COALESCE|ROUND|ABS|CEIL|FLOOR|SUBSTRING|TRIM|UPPER|LOWER|CONCAT'
|
|
64
|
+
const SQL_RULES_FIRST = [
|
|
65
|
+
{
|
|
66
|
+
match: `(?i:\\b(IS(?:\\s+NOT)?|NOT)\\s+(${CONSTANTS})\\b)`,
|
|
67
|
+
captures: { 1: { name: 'keyword.other.sql' }, 2: { name: 'constant.language.sql' } },
|
|
68
|
+
},
|
|
69
|
+
{ match: `(?i:\\b(?:${CONSTANTS})\\b)`, name: 'constant.language.sql' },
|
|
70
|
+
{ match: '(?i:\\b(?:EXPLAIN|ANALYZE)\\b)', name: 'keyword.other.sql' },
|
|
71
|
+
]
|
|
72
|
+
const SQL_RULES_LAST = [
|
|
73
|
+
{ match: '\\b[A-Za-z_][A-Za-z0-9_]*(?=\\()', name: 'support.function.sql' },
|
|
74
|
+
{ match: `(?i:\\b(?:${FUNCTIONS})\\b)`, name: 'support.function.sql' },
|
|
75
|
+
]
|
|
112
76
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const s = SQL_ALL.test(trimmed)
|
|
121
|
-
? getSqlStyle(trimmed)
|
|
122
|
-
: { color: '#4C4F69', light: '#4C4F69', dark: '#CDD6F4' }
|
|
123
|
-
const leading = token.content.slice(0, token.content.indexOf(trimmed))
|
|
124
|
-
const trailing = token.content.slice(token.content.indexOf(trimmed) + trimmed.length)
|
|
125
|
-
if (leading) newTokens.push({ ...token, content: leading, fontStyle: 0, htmlStyle: { color: '#4C4F69', '--shiki-light': '#4C4F69', '--shiki-dark': '#CDD6F4' } })
|
|
126
|
-
newTokens.push({
|
|
127
|
-
...token,
|
|
128
|
-
content: trimmed,
|
|
129
|
-
color: s.color,
|
|
130
|
-
fontStyle: 0,
|
|
131
|
-
htmlStyle: { color: s.color, '--shiki-light': s.light, '--shiki-dark': s.dark },
|
|
132
|
-
})
|
|
133
|
-
if (trailing) newTokens.push({ ...token, content: trailing, fontStyle: 0, htmlStyle: { color: '#4C4F69', '--shiki-light': '#4C4F69', '--shiki-dark': '#CDD6F4' } })
|
|
134
|
-
continue
|
|
135
|
-
}
|
|
77
|
+
// Colours: catppuccin-latte (light) and -mocha (dark), upright throughout (the
|
|
78
|
+
// template's code has no italics; Shiki's catppuccin sets some, e.g. on
|
|
79
|
+
// function names), with two SQL changes: numbers green, so that they differ
|
|
80
|
+
// from the orange constants (catppuccin colours both orange); and the table
|
|
81
|
+
// name after CREATE TABLE plain text, not a blue function name.
|
|
82
|
+
interface ThemeRule { scope?: string | string[], settings: { foreground?: string, fontStyle?: string } }
|
|
83
|
+
interface Theme { name: string, tokenColors?: ThemeRule[], [key: string]: unknown }
|
|
136
84
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
newTokens.push({
|
|
158
|
-
...token,
|
|
159
|
-
content: part,
|
|
160
|
-
offset: currOffset,
|
|
161
|
-
fontStyle: 0,
|
|
162
|
-
htmlStyle: { color: '#4C4F69', '--shiki-light': '#4C4F69', '--shiki-dark': '#CDD6F4' },
|
|
163
|
-
})
|
|
164
|
-
}
|
|
165
|
-
currOffset += part.length
|
|
166
|
-
}
|
|
167
|
-
continue
|
|
168
|
-
}
|
|
85
|
+
function variant(base: Theme, name: string, c: { constant: string, number: string, text: string }): Theme {
|
|
86
|
+
const upright = (base.tokenColors ?? []).map((rule) => {
|
|
87
|
+
const style = rule.settings.fontStyle
|
|
88
|
+
if (!style?.includes('italic'))
|
|
89
|
+
return rule
|
|
90
|
+
return { ...rule, settings: { ...rule.settings, fontStyle: style.replace('italic', '').trim() } }
|
|
91
|
+
})
|
|
92
|
+
return {
|
|
93
|
+
...base,
|
|
94
|
+
name,
|
|
95
|
+
tokenColors: [
|
|
96
|
+
...upright,
|
|
97
|
+
{ scope: 'constant.language.sql', settings: { foreground: c.constant } },
|
|
98
|
+
{ scope: 'constant.numeric.sql', settings: { foreground: c.number } },
|
|
99
|
+
{ scope: 'entity.name.function.sql', settings: { foreground: c.text } },
|
|
100
|
+
],
|
|
101
|
+
}
|
|
102
|
+
}
|
|
169
103
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
104
|
+
/** The theme's SQL grammar and its light and dark code colours (loaded once). */
|
|
105
|
+
export const highlighting = (() => {
|
|
106
|
+
let loaded: Promise<{ sql: any, light: Theme, dark: Theme }> | undefined
|
|
107
|
+
return () => loaded ??= (async () => {
|
|
108
|
+
const [[sql], latte, mocha] = await Promise.all([
|
|
109
|
+
bundledLanguages.sql().then(m => m.default as any[]),
|
|
110
|
+
bundledThemes['catppuccin-latte']().then(m => m.default as unknown as Theme),
|
|
111
|
+
bundledThemes['catppuccin-mocha']().then(m => m.default as unknown as Theme),
|
|
112
|
+
])
|
|
113
|
+
return {
|
|
114
|
+
sql: { ...sql, patterns: [...SQL_RULES_FIRST, ...sql.patterns, ...SQL_RULES_LAST] },
|
|
115
|
+
light: variant(latte, 'tud-db-light', { constant: '#fe640b', number: '#059669', text: '#4c4f69' }),
|
|
116
|
+
dark: variant(mocha, 'tud-db-dark', { constant: '#fab387', number: '#34d399', text: '#cdd6f4' }),
|
|
173
117
|
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
118
|
+
})()
|
|
119
|
+
})()
|
|
176
120
|
|
|
177
|
-
export default () =>
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
121
|
+
export default async () => {
|
|
122
|
+
const { sql, light, dark } = await highlighting()
|
|
123
|
+
return {
|
|
124
|
+
themes: { light, dark },
|
|
125
|
+
// replaces Shiki's `sql` with the extended grammar above
|
|
126
|
+
langs: [sql],
|
|
127
|
+
transformers: [codeMarks],
|
|
128
|
+
}
|
|
129
|
+
}
|
package/styles/db-theme.css
CHANGED
|
@@ -215,6 +215,14 @@ code.db-inline-code.with-bg {
|
|
|
215
215
|
width: fit-content;
|
|
216
216
|
max-width: 100%;
|
|
217
217
|
}
|
|
218
|
+
/* Code that morphs to the next slide (data-morph, from the gsap addon): while
|
|
219
|
+
the morph animates its box from the other slide's size, the code can be
|
|
220
|
+
wider than the box for a moment, and Slidev's `overflow: auto` on code
|
|
221
|
+
blocks would flash a scrollbar. Let it overflow instead; it fits again once
|
|
222
|
+
the morph ends. */
|
|
223
|
+
.slidev-layout [data-morph] .slidev-code {
|
|
224
|
+
overflow: visible;
|
|
225
|
+
}
|
|
218
226
|
/* Default to transparent background for multi-line blocks, overriding Shiki */
|
|
219
227
|
.slidev-layout.db-default pre.slidev-code {
|
|
220
228
|
background: transparent !important;
|
package/vite.config.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHighlighter } from 'shiki'
|
|
2
2
|
import { setupTableCaptions } from './markdown.ts'
|
|
3
|
-
import {
|
|
3
|
+
import { highlighting } from './setup/shiki.ts'
|
|
4
4
|
|
|
5
5
|
// Shipped WITH the theme: Slidev merges vite.config from every root (theme +
|
|
6
6
|
// addons + deck), so installing this theme is enough — no need to copy a file.
|
|
@@ -8,26 +8,40 @@ import { sqlKeywords } from './setup/shiki.ts'
|
|
|
8
8
|
// Inline code syntax highlighting. Write inline code as: `{lang} the code`
|
|
9
9
|
// e.g. `{json} {"a": 1}`
|
|
10
10
|
// The leading `{lang}` token selects the language; everything after the first
|
|
11
|
-
// space is highlighted with Shiki and rendered inside a normal inline <code
|
|
11
|
+
// space is highlighted with Shiki and rendered inside a normal inline <code>,
|
|
12
|
+
// with the same SQL grammar and colours as fenced code (setup/shiki.ts).
|
|
12
13
|
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
14
|
+
// Untagged inline code is highlighted too when it is clearly one of:
|
|
15
|
+
// - SQL: it has an UPPERCASE SQL word (`NULL`, `GROUP BY`, `TRUE AND UNKNOWN`)
|
|
16
|
+
// or is a lone comparison operator (`<>`, `<=>`). Uppercase only, so that
|
|
17
|
+
// code from other languages (`std::set`, `end()`, `{lines:true}`) stays plain.
|
|
18
|
+
// - a function call, `next()` or `child.next()`: the name in function blue.
|
|
15
19
|
//
|
|
16
|
-
// A deck can still override
|
|
20
|
+
// A deck can still override LANGS or the rule by defining its own
|
|
17
21
|
// vite.config.ts (the deck's config merges last and wins).
|
|
18
|
-
const
|
|
19
|
-
const LANGS = ['json', 'yaml', 'sql', 'c', 'cpp', 'python']
|
|
22
|
+
const LANGS = ['json', 'yaml', 'c', 'cpp', 'python']
|
|
20
23
|
|
|
21
|
-
const AUTO_SQL = /\b(TRUE|FALSE|UNKNOWN|NULL|EXPLAIN|ANALYZE|COUNT|AVG|SUM|MIN|MAX|COALESCE|SELECT|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|JOIN|LEFT|RIGHT|INNER|OUTER|FULL|CROSS|ON|USING|LIMIT|OFFSET|UNION|INSERT|UPDATE|DELETE|CREATE|DROP|ALTER|TABLE|INTO|VALUES|SET|BETWEEN|IN|NOT|AND|OR|IS|LIKE|ILIKE|AS|CASE|WHEN|THEN|ELSE|END)\b/
|
|
24
|
+
const AUTO_SQL = /\b(?:TRUE|FALSE|UNKNOWN|NULL|EXPLAIN|ANALYZE|COUNT|AVG|SUM|MIN|MAX|COALESCE|SELECT|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|JOIN|LEFT|RIGHT|INNER|OUTER|FULL|CROSS|ON|USING|LIMIT|OFFSET|UNION|INSERT|UPDATE|DELETE|CREATE|DROP|ALTER|TABLE|INTO|VALUES|SET|BETWEEN|IN|NOT|AND|OR|IS|LIKE|ILIKE|AS|CASE|WHEN|THEN|ELSE|END)\b/
|
|
22
25
|
const AUTO_SQL_OPS = /^(?:<=>|<>|<=|>=|!=|=|<|>)$/
|
|
26
|
+
const CALL = /^((?:[a-z_]\w*\.)*)([a-z_]\w*)(\s*\(.*\))$/i
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
const escapeHtml = (s: string) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
29
|
+
|
|
30
|
+
// Vue compiles each slide as a template and condenses whitespace: it drops a
|
|
31
|
+
// text node that is only spaces at the start or end of an element (Shiki's
|
|
32
|
+
// span for a lone space), and shrinks runs of spaces to one. Protect exactly
|
|
33
|
+
// those spaces with ; every other space stays a normal one, so long
|
|
34
|
+
// inline code can still wrap.
|
|
35
|
+
function keepSpaces(html: string): string {
|
|
36
|
+
return html
|
|
37
|
+
.replace(/>( +)</g, (_m, sp: string) => `>${' '.repeat(sp.length)}<`)
|
|
38
|
+
.replace(/ {2,}/g, sp => `${' '.repeat(sp.length - 1)} `)
|
|
27
39
|
}
|
|
28
40
|
|
|
29
41
|
export default async () => {
|
|
30
|
-
const
|
|
42
|
+
const { sql, light } = await highlighting()
|
|
43
|
+
const highlighter = await createHighlighter({ themes: [light], langs: [...LANGS, sql] })
|
|
44
|
+
const THEME = light.name
|
|
31
45
|
|
|
32
46
|
return {
|
|
33
47
|
slidev: {
|
|
@@ -39,45 +53,34 @@ export default async () => {
|
|
|
39
53
|
md.renderer.rules.code_inline = (tokens: any[], idx: number, options: any, env: any, self: any) => {
|
|
40
54
|
const raw = String(tokens[idx].content)
|
|
41
55
|
const m = raw.match(/^\{([\w-]+)\}\s+([\s\S]+)$/)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// Auto-detect common SQL keywords/constants when untagged
|
|
46
|
-
if (!lang && isAutoSql(code)) {
|
|
47
|
-
lang = 'sql'
|
|
48
|
-
}
|
|
56
|
+
const code = m ? m[2] : raw
|
|
57
|
+
const bare = code.trim()
|
|
58
|
+
const lang = m ? m[1] : (AUTO_SQL_OPS.test(bare) || AUTO_SQL.test(bare)) ? 'sql' : null
|
|
49
59
|
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
inner += `<span style="color:#4C4F69;--shiki-light:#4C4F69;--shiki-dark:#CDD6F4">${prefix}</span>`
|
|
59
|
-
}
|
|
60
|
-
inner += `<span style="color:#1E66F5;--shiki-light:#1E66F5;--shiki-dark:#89B4FA">${fnName}</span>`
|
|
61
|
-
inner += `<span style="color:#7C7F93;--shiki-light:#7C7F93;--shiki-dark:#9399B2">${rest}</span>`
|
|
62
|
-
return `<code class="db-inline-code">${inner}</code>`
|
|
60
|
+
// untagged function call: the name in function blue, the rest in
|
|
61
|
+
// the colour of punctuation (as Shiki colours `()` in code)
|
|
62
|
+
const call = !lang && bare.match(CALL)
|
|
63
|
+
if (call) {
|
|
64
|
+
const [, prefix, name, rest] = call
|
|
65
|
+
const span = (color: string, text: string) => text ? `<span style="color:${color}">${escapeHtml(text)}</span>` : ''
|
|
66
|
+
const inner = span('#4C4F69', prefix) + span('#1E66F5', name) + span('#7C7F93', rest)
|
|
67
|
+
return `<code class="db-inline-code">${keepSpaces(inner)}</code>`
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
if (lang && highlighter.getLoadedLanguages().includes(lang)) {
|
|
66
|
-
|
|
67
|
-
.codeToHtml(code, { lang, theme: THEME
|
|
71
|
+
const inner = highlighter
|
|
72
|
+
.codeToHtml(code, { lang, theme: THEME })
|
|
68
73
|
.replace(/^<pre[^>]*><code[^>]*>/, '')
|
|
69
74
|
.replace(/<\/code><\/pre>\s*$/, '')
|
|
70
|
-
|
|
71
|
-
// Preserve all spaces across tokens so Vue's template compiler never condenses/strips them
|
|
72
|
-
inner = inner.replace(/<span([^>]*)>([^<]*)<\/span>/g, (_m, attrs, text) => {
|
|
73
|
-
return `<span${attrs}>${text.replace(/ /g, ' ')}</span>`
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
return `<code class="db-inline-code">${inner}</code>`
|
|
75
|
+
return `<code class="db-inline-code">${keepSpaces(inner)}</code>`
|
|
77
76
|
}
|
|
78
|
-
|
|
77
|
+
// plain inline code: unhighlighted, but its spaces kept too. (Slidev
|
|
78
|
+
// renders markdown asynchronously, so the default rule may return
|
|
79
|
+
// a promise of the HTML.)
|
|
80
|
+
const plain = fallback
|
|
79
81
|
? fallback(tokens, idx, options, env, self)
|
|
80
82
|
: self.renderToken(tokens, idx, options)
|
|
83
|
+
return typeof plain === 'string' ? keepSpaces(plain) : Promise.resolve(plain).then(keepSpaces)
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
// ASCII arrows -> unicode, in plain TEXT only. Code spans/blocks are
|