slidev-theme-kotlin 0.13.2 → 0.14.1
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 +38 -1
- package/README.md +396 -633
- package/assets/gutter/suspendCall@14x14.svg +6 -0
- package/assets/gutter/suspendCall@14x14_dark.svg +6 -0
- package/bin/present.mjs +296 -0
- package/components/DrawnAnnotation.vue +57 -9
- package/components/DrawnAnnotationEditorToolbar.vue +1 -1
- package/components/InlineCompilerError.vue +46 -15
- package/components/InlineValue.vue +3 -3
- package/components/MagicMoveBetween.vue +6 -4
- package/components/README.md +60 -2
- package/components/SmartCast.vue +3 -3
- package/components/SuspendGutter.vue +168 -0
- package/components/TypeHint.vue +3 -3
- package/components/Warning.vue +3 -3
- package/components/carried-layer.ts +29 -0
- package/components/code-highlight.ts +119 -5
- package/components/transition-ink.ts +27 -6
- package/docs/annotation-editor.png +0 -0
- package/docs/code-marks.md +76 -0
- package/docs/phone-remote.png +0 -0
- package/docs/presenting.md +122 -0
- package/docs/publishing.md +111 -0
- package/docs/snippets.md +84 -0
- package/global-top.vue +3 -8
- package/package.json +11 -4
- package/remote/RemoteControl.vue +278 -0
- package/remote/connection.ts +19 -0
- package/setup/code-imports.ts +3 -2
- package/setup/code-marks.ts +562 -0
- package/setup/kotlin-snippets.ts +4 -2
- package/setup/magic-move-between.ts +10 -2
- package/setup/main.ts +5 -1
- package/setup/remote-sync.ts +226 -0
- package/setup/routes.ts +21 -0
- package/setup/static-content.ts +39 -5
- package/setup/transformers.ts +16 -1
- package/styles/layout.css +17 -0
- package/assets/edit-3-svgrepo-com.svg +0 -5
- package/components/DrawnAnnotationEditorLauncher.vue +0 -59
package/README.md
CHANGED
|
@@ -1,139 +1,91 @@
|
|
|
1
1
|
# Slidev Theme Kotlin
|
|
2
2
|
|
|
3
|
-
A [Slidev](https://sli.dev) theme
|
|
3
|
+
A [Slidev](https://sli.dev) theme for Kotlin talks: code windows that look
|
|
4
|
+
like IntelliJ, the decorations your IDE shows, hand-drawn annotations, and
|
|
5
|
+
Kodee.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
**[See the demo deck](https://nomisrev.github.io/slidev-theme-kotlin/)**. Its
|
|
8
|
+
source is [`slides.md`](slides.md), so every slide doubles as an example.
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
## Quick start
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
- **Magic Move Animations**: Smooth transitions for Kodee between slides with configurable size and position
|
|
11
|
-
- **Magic Move between slides**: Link slides with a `magic-move` separator and their code windows morph across the slide boundary, including several parallel snippets per slide
|
|
12
|
-
- **Kotlin Syntax Highlighting**: Custom Shiki configuration optimized for Kotlin code
|
|
13
|
-
- **Multiple Layouts**: Pre-built layouts including default, cover, and intro
|
|
14
|
-
- **JetBrains Fonts**: Prefers locally installed JetBrains Sans and JetBrains Mono, with Inter and Google-hosted JetBrains Mono fallbacks
|
|
15
|
-
- **Drawn annotations**: Click-aware, hand-drawn marks, labels, and connectors for elements and code
|
|
16
|
-
- **Inline compiler diagnostics**: IntelliJ-style squiggles and messages for static and Magic Move code blocks
|
|
17
|
-
|
|
18
|
-
## Requirements
|
|
19
|
-
|
|
20
|
-
- **Node.js**: >= 20.12.0
|
|
21
|
-
- **Package Manager**: npm (or pnpm with shamefully-hoist=true)
|
|
22
|
-
|
|
23
|
-
## Installation
|
|
24
|
-
|
|
25
|
-
Install the theme in your Slidev project:
|
|
12
|
+
You need Node.js 20.12 or newer. Install the theme in your Slidev project:
|
|
26
13
|
|
|
27
14
|
```bash
|
|
28
15
|
npm install slidev-theme-kotlin
|
|
29
16
|
```
|
|
30
17
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
### Visual annotation editor (development)
|
|
34
|
-
|
|
35
|
-
Enable the optional, development-only source writer in the consuming deck's
|
|
36
|
-
`vite.config.ts`:
|
|
37
|
-
|
|
38
|
-
```ts
|
|
39
|
-
import { defineConfig } from 'vite'
|
|
40
|
-
import { drawnAnnotationEditor } from 'slidev-theme-kotlin/annotation-editor'
|
|
41
|
-
|
|
42
|
-
export default defineConfig({
|
|
43
|
-
plugins: [drawnAnnotationEditor()],
|
|
44
|
-
})
|
|
45
|
-
```
|
|
18
|
+
With pnpm, set `shamefully-hoist=true` in `.npmrc`.
|
|
46
19
|
|
|
47
|
-
|
|
48
|
-
edit session on the current slide and click state: Slidev navigation — arrow
|
|
49
|
-
keys, space, page keys, click-to-navigate, swipes — is paused until you leave
|
|
50
|
-
the editor. Drag a visible label, its width handle, or a connector control, or
|
|
51
|
-
nudge the selected control with the arrow keys (**Shift** for larger steps);
|
|
52
|
-
every change stays a local draft, so nothing is written mid-gesture. **Done
|
|
53
|
-
editing annotations** commits all changed drafts as normalized `:geometry`
|
|
54
|
-
bindings on exactly the edited `DrawnAnnotation` tags, then exits. **Esc**
|
|
55
|
-
cancels in stages — an active drag, then the selection, then (after a
|
|
56
|
-
confirmation) the whole session, discarding its drafts. One toolbar control
|
|
57
|
-
hides or shows the connector (writing `:connect` into the tag where the
|
|
58
|
-
deck's default differs, see `themeConfig.drawnAnnotation.connect`), one
|
|
59
|
-
switches it between manual and automatic routing, another curves or
|
|
60
|
-
straightens it with a draggable quadratic control point, and **Reset
|
|
61
|
-
annotation** or **Reset slide** removes the saved geometry of one annotation
|
|
62
|
-
or of every annotation on the slide. Source revisions are checked, so a
|
|
63
|
-
conflicting commit fails without overwriting anyone: the drafts stay visible,
|
|
64
|
-
and you either fix and press Done again or choose **Reload saved geometry** to
|
|
65
|
-
discard them. The locator, toolbar, and write endpoint exist only while Vite
|
|
66
|
-
serves the deck; builds and exports render saved geometry without editor code.
|
|
67
|
-
|
|
68
|
-
While serving, an annotation that cannot find what it should mark (a `text`
|
|
69
|
-
that no longer matches the fence, an unmatched `selector` or `target`, an
|
|
70
|
-
`occurrence` past the last match, or an `InlineCompilerError`, `SmartCast`, `Warning`,
|
|
71
|
-
`TypeHint`, or `InlineValue` whose target is not found) also puts a red chip in the top-left corner of its slide, next to
|
|
72
|
-
the warning it prints on the console. Hover the chip for the full message. The
|
|
73
|
-
chip appears only once the annotation's click has been reached and the slide
|
|
74
|
-
has settled, so a Magic Move step that does not yet contain the text never
|
|
75
|
-
flags one, and it disappears again when the target turns up. Builds, exports,
|
|
76
|
-
the presenter view, and print mode never render it.
|
|
77
|
-
|
|
78
|
-
### Basic Setup
|
|
79
|
-
|
|
80
|
-
Add the theme to your slides' frontmatter:
|
|
20
|
+
Then name the theme at the top of `slides.md`:
|
|
81
21
|
|
|
82
22
|
```yaml
|
|
83
23
|
---
|
|
84
24
|
theme: kotlin
|
|
85
25
|
transition: fade
|
|
26
|
+
themeConfig:
|
|
27
|
+
kodee: greeting
|
|
86
28
|
---
|
|
87
29
|
|
|
88
30
|
# Your Presentation Title
|
|
89
31
|
```
|
|
90
32
|
|
|
91
|
-
|
|
33
|
+
## Contents
|
|
34
|
+
|
|
35
|
+
- [Code windows](#code-windows)
|
|
36
|
+
- [Folded imports](#folded-imports)
|
|
37
|
+
- [Magic Move between slides](#magic-move-between-slides)
|
|
38
|
+
- [IDE decorations](#ide-decorations)
|
|
39
|
+
- [Drawn annotations](#drawn-annotations)
|
|
40
|
+
- [Kodee](#kodee)
|
|
41
|
+
- [Layouts](#layouts)
|
|
42
|
+
- [Customising](#customising)
|
|
43
|
+
- [Publishing a deck](#publishing-a-deck)
|
|
44
|
+
- [Presenting from your phone](#presenting-from-your-phone)
|
|
45
|
+
- [Compiling the snippets](#compiling-the-snippets)
|
|
46
|
+
- [Tips](#tips)
|
|
47
|
+
- [Developing the theme](#developing-the-theme)
|
|
92
48
|
|
|
93
|
-
Code
|
|
94
|
-
|
|
95
|
-
|
|
49
|
+
## Code windows
|
|
50
|
+
|
|
51
|
+
Every code fence is drawn as an IntelliJ editor, Light or Darcula with the
|
|
52
|
+
deck's colour scheme. An icon in the top-right corner and the border colour
|
|
53
|
+
say what kind of code it is:
|
|
96
54
|
|
|
97
55
|
````md
|
|
98
56
|
```kotlin
|
|
99
57
|
fun main() = println("Hello")
|
|
100
58
|
```
|
|
101
59
|
|
|
102
|
-
```
|
|
60
|
+
```kotlin gradle
|
|
103
61
|
plugins { kotlin("jvm") }
|
|
104
62
|
```
|
|
63
|
+
````
|
|
105
64
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
65
|
+
| Fence | Icon |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| `kotlin`, `kt`, `kts` | Kotlin |
|
|
68
|
+
| `kotlin gradle` | Gradle |
|
|
69
|
+
| `kotlin ktor` | Ktor |
|
|
70
|
+
| `kotlin arrow` | [Arrow](https://arrow-kt.io) |
|
|
71
|
+
| `kotlin jdbc`, `kotlin r2dbc` | JDBC, R2DBC |
|
|
72
|
+
| `java` | Java |
|
|
73
|
+
| `bash` | Terminal |
|
|
74
|
+
| `sql` | PostgreSQL |
|
|
75
|
+
| `yaml toolchain` | Amper |
|
|
76
|
+
| `xml maven` | Maven |
|
|
109
77
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
```
|
|
78
|
+
The modifiers work with `kt` and `kts` too, and inside Magic Move. Any other
|
|
79
|
+
fence is a plain code window.
|
|
113
80
|
|
|
114
|
-
|
|
115
|
-
SELECT * FROM presentations;
|
|
116
|
-
```
|
|
117
|
-
````
|
|
81
|
+
### Program output
|
|
118
82
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
with an `arrow` modifier the [Arrow](https://arrow-kt.io) icon, for Kotlin
|
|
122
|
-
code that is really about `Raise`, `Resource`, or another Arrow library.
|
|
123
|
-
Use `kotlin ktor` (also `kt ktor` or `kts ktor`) for the Ktor icon and purple
|
|
124
|
-
border on Ktor code. These modifiers also work with Magic Move.
|
|
125
|
-
`yaml toolchain`, `java`, `bash`, `xml maven`, and `sql` use the Amper, Java,
|
|
126
|
-
Terminal, Maven, and PostgreSQL icons respectively. All other fences remain plain
|
|
127
|
-
code surfaces.
|
|
128
|
-
|
|
129
|
-
A `console` fence is program output, not code: it renders as a flat panel in
|
|
130
|
-
the colours of IntelliJ's Run tool window, without the editor's outline and
|
|
131
|
-
shadow, marked with the green run icon. Put it under the Kotlin fence whose
|
|
132
|
-
output it shows:
|
|
83
|
+
A `console` fence is output, not code. It looks like IntelliJ's Run tool
|
|
84
|
+
window. Put it under the fence whose output it shows:
|
|
133
85
|
|
|
134
86
|
````md
|
|
135
87
|
```kotlin
|
|
136
|
-
println(
|
|
88
|
+
println(listOf(1, 2, 3).map { it + 1 })
|
|
137
89
|
```
|
|
138
90
|
|
|
139
91
|
```console
|
|
@@ -141,51 +93,11 @@ println([1, 2, 3].map { it + 1 })
|
|
|
141
93
|
```
|
|
142
94
|
````
|
|
143
95
|
|
|
144
|
-
|
|
145
|
-
does, wrap the fence in [`InlineValue`](components/README.md#inlinevalue).
|
|
146
|
-
The other IntelliJ decorations have a component each: [`SmartCast`](components/README.md#smartcast)
|
|
147
|
-
paints the green smart-cast background behind exact code, [`Warning`](components/README.md#warning)
|
|
148
|
-
the yellow warning background with an optional message, and [`TypeHint`](components/README.md#typehint)
|
|
149
|
-
the grey `this: HTML` inlay hint after a lambda's brace.
|
|
96
|
+
## Folded imports
|
|
150
97
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
```css
|
|
156
|
-
:root {
|
|
157
|
-
--code-window-kotlin-color: #834df0;
|
|
158
|
-
--code-window-gradle-color: #6c707e;
|
|
159
|
-
--code-window-amper-color: #087cfa;
|
|
160
|
-
--code-window-arrow-color: #2455cf;
|
|
161
|
-
--code-window-ktor-color: #7f52ff;
|
|
162
|
-
--code-window-java-color: #e66d17;
|
|
163
|
-
--code-window-terminal-color: #6c707e;
|
|
164
|
-
--code-window-maven-color: #3574f0;
|
|
165
|
-
--code-window-postgresql-color: #336791;
|
|
166
|
-
--code-window-jdbc-color: #f59e0b;
|
|
167
|
-
--code-window-r2dbc-color: #06b6d4;
|
|
168
|
-
|
|
169
|
-
/* The `console` output panel. */
|
|
170
|
-
--code-window-output-background: #f7f8fa;
|
|
171
|
-
--code-window-output-color: #2b2d30;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
html.dark {
|
|
175
|
-
--code-window-kotlin-color: #a571e6;
|
|
176
|
-
--code-window-output-background: #1e1f22;
|
|
177
|
-
--code-window-output-color: #bcbec4;
|
|
178
|
-
/* Override any of the other dark-mode values as needed. */
|
|
179
|
-
}
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### Folded imports
|
|
183
|
-
|
|
184
|
-
A snippet on a slide reads best without its imports, but a snippet that
|
|
185
|
-
carries them is complete: it compiles as written, a snippet checker such as
|
|
186
|
-
Knit can verify it, and the deck's handout (see below) shows a reader which
|
|
187
|
-
package every symbol comes from. So write the imports, and the theme folds
|
|
188
|
-
them away:
|
|
98
|
+
A snippet reads best without its imports, but a snippet that has them is
|
|
99
|
+
complete: it compiles, and a reader of the handout can see where every symbol
|
|
100
|
+
comes from. So write the imports, and the theme hides them on the slide:
|
|
189
101
|
|
|
190
102
|
````md
|
|
191
103
|
```kotlin
|
|
@@ -196,12 +108,14 @@ fun numbers(): Flow<Int> = flowOf(1, 2, 3)
|
|
|
196
108
|
```
|
|
197
109
|
````
|
|
198
110
|
|
|
199
|
-
|
|
200
|
-
|
|
111
|
+
The slide shows only `fun numbers()…`. The handout and `llms-full.txt` print
|
|
112
|
+
the fence in full.
|
|
113
|
+
|
|
114
|
+
### Hide more than imports
|
|
201
115
|
|
|
202
|
-
When an example needs
|
|
203
|
-
|
|
204
|
-
|
|
116
|
+
When an example needs a table definition or a helper, put a `// Example`
|
|
117
|
+
comment where the example starts. Everything above it stays off the slide,
|
|
118
|
+
and so does the comment:
|
|
205
119
|
|
|
206
120
|
````md
|
|
207
121
|
```kotlin
|
|
@@ -216,16 +130,14 @@ fun doubled(): Flow<Int> = numbers().map { it * 2 }
|
|
|
216
130
|
```
|
|
217
131
|
````
|
|
218
132
|
|
|
219
|
-
The
|
|
220
|
-
fence still compiles as written (see the snippet checker below), and the
|
|
221
|
-
handout keeps it. A description after the colon is optional.
|
|
133
|
+
The text after the colon is optional.
|
|
222
134
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
the
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
reveal it:
|
|
135
|
+
### Hide the answer of an exercise
|
|
136
|
+
|
|
137
|
+
Put the answer above a `// Solution` comment. It folds like `// Example`, and
|
|
138
|
+
the snippet checker compiles the assertions against it. The handout and
|
|
139
|
+
`llms-full.txt` leave the answer out (they keep its `import` lines), and
|
|
140
|
+
nothing on the slide can reveal it:
|
|
229
141
|
|
|
230
142
|
````md
|
|
231
143
|
```kotlin
|
|
@@ -237,51 +149,26 @@ check(!7.isEven())
|
|
|
237
149
|
```
|
|
238
150
|
````
|
|
239
151
|
|
|
240
|
-
Details
|
|
241
|
-
|
|
242
|
-
- Kotlin, Java, and Scala fences (`kotlin`, `kt`, `kts`, `java`, `scala`)
|
|
243
|
-
fold. Without a marker, only a block of `import` lines at the top of the
|
|
244
|
-
fence counts, and a `package` line or `@file:` annotation before it stays
|
|
245
|
-
visible. With a marker, everything above it folds. Only the first
|
|
246
|
-
`// Example` or `// Solution` line in a fence counts, and only when there
|
|
247
|
-
is code above it.
|
|
248
|
-
- Line numbers, `{1|2-3}` highlight ranges, and annotation targets all
|
|
249
|
-
count from the first line of real code, so folding never shifts them.
|
|
250
|
-
- Cross-slide Magic Move chains and classic ````md magic-move```` blocks fold
|
|
251
|
-
too, step by step.
|
|
252
|
-
- Add `show-imports` to a fence (`` ```kotlin show-imports ``) when the slide
|
|
253
|
-
is about the imports themselves, or set `themeConfig: { foldImports: false }`
|
|
254
|
-
in the headmatter to keep every import visible.
|
|
255
|
-
|
|
256
|
-
### Code sizing
|
|
257
|
-
|
|
258
|
-
Every code window uses one font size calculated from the slide canvas width. By
|
|
259
|
-
default, a full-width window fits 80 monospace characters. Override the column
|
|
260
|
-
count once in the deck's stylesheet, or narrower on a single slide class:
|
|
261
|
-
|
|
262
|
-
```css
|
|
263
|
-
:root {
|
|
264
|
-
--code-window-columns: 72;
|
|
265
|
-
}
|
|
266
|
-
```
|
|
152
|
+
### Details
|
|
267
153
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
first
|
|
276
|
-
|
|
277
|
-
|
|
154
|
+
- Kotlin, Java, and Scala fences fold (`kotlin`, `kt`, `kts`, `java`,
|
|
155
|
+
`scala`).
|
|
156
|
+
- Without a marker, only the block of `import` lines at the top folds. A
|
|
157
|
+
`package` line or `@file:` annotation before it stays visible.
|
|
158
|
+
- Only the first `// Example` or `// Solution` line in a fence counts, and
|
|
159
|
+
only when there is code above it.
|
|
160
|
+
- Line numbers, `{1|2-3}` highlight ranges, and annotation targets count from
|
|
161
|
+
the first visible line, so folding never shifts them.
|
|
162
|
+
- Magic Move folds too, step by step.
|
|
163
|
+
- To show the imports, add `show-imports` to one fence
|
|
164
|
+
(`` ```kotlin show-imports ``) or set `themeConfig: { foldImports: false }`
|
|
165
|
+
for the whole deck.
|
|
278
166
|
|
|
279
|
-
|
|
167
|
+
## Magic Move between slides
|
|
280
168
|
|
|
281
|
-
Slidev's
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
the next when you navigate.
|
|
169
|
+
Slidev's Magic Move animates code inside one slide. This theme can also morph
|
|
170
|
+
code **from one slide to the next**: put `magic-move` in the separator
|
|
171
|
+
between them.
|
|
285
172
|
|
|
286
173
|
````md
|
|
287
174
|
# My Slide
|
|
@@ -301,125 +188,85 @@ class Person(val name: String)
|
|
|
301
188
|
```
|
|
302
189
|
````
|
|
303
190
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
-
|
|
313
|
-
|
|
314
|
-
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
overview, and PDF/PNG exports render each slide's own step statically.
|
|
326
|
-
- Fence metadata still works: the code-window identity icons, `[Title]`
|
|
327
|
-
labels, and `{lines:true}` line numbers apply per chain. Per-click line
|
|
328
|
-
highlighting works too — `` ```kotlin {1|2-3} `` registers clicks on that
|
|
329
|
-
slide and steps through the ranges before navigation moves on, exactly like
|
|
330
|
-
one step of a classic Magic Move block. Snippet imports (`<<< @/...`) do
|
|
331
|
-
not participate in chains.
|
|
332
|
-
- `magicMove: true` in the slide's YAML frontmatter is equivalent to the bare
|
|
333
|
-
`magic-move` separator and can be combined with other frontmatter keys.
|
|
334
|
-
|
|
335
|
-
### Using Kodee Mascot
|
|
336
|
-
|
|
337
|
-
Enable Kodee once for the whole deck with `themeConfig`:
|
|
338
|
-
|
|
339
|
-
```yaml
|
|
340
|
-
---
|
|
341
|
-
theme: kotlin
|
|
342
|
-
themeConfig:
|
|
343
|
-
kodee: greeting
|
|
344
|
-
---
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
Kodee then uses layout-aware defaults: `small` in the bottom-right corner on regular slides, and `large`/`featured` on `cover` and `intro` slides. Override only the variant when a slide needs a different expression:
|
|
348
|
-
|
|
349
|
-
```yaml
|
|
350
|
-
---
|
|
351
|
-
kodee: wink
|
|
352
|
-
---
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
Use `kodee: false` to hide the mascot on one slide. The original object form remains available when you need complete control:
|
|
191
|
+
- Repeat the separator to chain any number of slides. The last slide of a
|
|
192
|
+
chain needs no marker.
|
|
193
|
+
- Code windows pair up by position: the first fence morphs into the first
|
|
194
|
+
fence of the next slide, the second into the second. Kotlin next to SQL
|
|
195
|
+
animates side by side.
|
|
196
|
+
- Everything outside the fences is a normal slide. Headings and text can
|
|
197
|
+
change freely.
|
|
198
|
+
- Navigating backward plays the animation in reverse.
|
|
199
|
+
- Icons, `[Title]` labels, `{lines:true}`, and click highlighting such as
|
|
200
|
+
`` ```kotlin {1|2-3} `` all work.
|
|
201
|
+
- `magicMove: true` in a slide's frontmatter does the same as the bare
|
|
202
|
+
separator, and combines with other frontmatter keys.
|
|
203
|
+
- Snippet imports (`<<< @/...`) do not take part in a chain.
|
|
204
|
+
- Use `transition: fade`. With `transition: view-transition`, Slidev 52
|
|
205
|
+
stalls for a moment on many navigations.
|
|
206
|
+
|
|
207
|
+
## IDE decorations
|
|
208
|
+
|
|
209
|
+
Show what the IDE shows: smart casts, warnings, errors, receiver hints,
|
|
210
|
+
debugger values, and suspension points. Ask for one with a trailing
|
|
211
|
+
`// [!kind …]` comment on the line it belongs to:
|
|
356
212
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
On `cover` and `intro` slides the text sits in a column beside the featured
|
|
367
|
-
mascot and wraps inside it instead of running under Kodee. The column is
|
|
368
|
-
`--cover-text-width`, 48% of the content box by default, which clears the
|
|
369
|
-
mascot's arm. Widen it in the deck's stylesheet when the titles are short, or
|
|
370
|
-
give a slide that hides the mascot the full width:
|
|
371
|
-
|
|
372
|
-
```css
|
|
373
|
-
.slidev-layout.cover.no-mascot {
|
|
374
|
-
--cover-text-width: 100%;
|
|
213
|
+
````md
|
|
214
|
+
```kotlin
|
|
215
|
+
fun Application.users() = routing { // [!this Routing]
|
|
216
|
+
route(Api.Users.create) { // [!this TypedRespondScope<CreateUser, User>]
|
|
217
|
+
when (val result = insertUser(body)) {
|
|
218
|
+
is Success -> respond(result.user) // [!smartcast result]
|
|
219
|
+
AlreadyExists -> fail(UserAlreadyExists)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
375
222
|
}
|
|
376
223
|
```
|
|
224
|
+
````
|
|
377
225
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
- **variant**: Choose from available Kodee images
|
|
381
|
-
- `greeting` - Kodee waving hello
|
|
382
|
-
- `wink` - Kodee winking
|
|
383
|
-
- `wave` - Kodee waving
|
|
384
|
-
- `jumping` - Kodee jumping with joy
|
|
385
|
-
- `sitting` - Kodee sitting down
|
|
386
|
-
- `drinking` - Kodee with a drink
|
|
387
|
-
- `heart` - Kodee with a heart
|
|
388
|
-
- `in-love` - Kodee in love
|
|
389
|
-
- `welcome` - Kodee welcoming
|
|
390
|
-
- `winter` - Kodee in winter attire
|
|
391
|
-
- `tiny` - Tiny Kodee
|
|
226
|
+
The comment never reaches the slide, the handout, or the compiled snippets.
|
|
392
227
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
228
|
+
| Mark | What appears |
|
|
229
|
+
| --- | --- |
|
|
230
|
+
| `[!smartcast TEXT]` | Green smart-cast background behind `TEXT` |
|
|
231
|
+
| `[!warning TEXT MESSAGE]` | Yellow warning background, with the message after the line |
|
|
232
|
+
| `[!error TEXT MESSAGE]` | Red squiggle under `TEXT`, with the message after the line |
|
|
233
|
+
| `[!this TYPE]` / `[!context TYPE]` | Grey `this: TYPE` hint after the line's `{` |
|
|
234
|
+
| `[!type NAME TYPE]` | Inferred-type hint after `NAME` |
|
|
235
|
+
| `[!value TEXT VALUE]` | Debugger value after the line |
|
|
236
|
+
| `[!suspend]` | Suspend-call icon in the gutter |
|
|
397
237
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
- `featured` - Prominently displayed (cover/intro default)
|
|
401
|
-
- `custom` - Use with `x` and `y` coordinates
|
|
238
|
+
Quoting, options such as `on=1`, and several marks on one line are covered in
|
|
239
|
+
[`docs/code-marks.md`](docs/code-marks.md).
|
|
402
240
|
|
|
403
|
-
|
|
241
|
+
### As components
|
|
404
242
|
|
|
405
|
-
|
|
243
|
+
Each decoration is also a component that wraps one fence. Use it when the
|
|
244
|
+
decoration should appear on a click, or around a classic ````md magic-move````
|
|
245
|
+
block:
|
|
406
246
|
|
|
407
|
-
|
|
247
|
+
````md
|
|
248
|
+
<InlineCompilerError text="userName" message="Unresolved reference: userName" :on="1">
|
|
408
249
|
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
kodee:
|
|
412
|
-
variant: greeting
|
|
413
|
-
position: custom
|
|
414
|
-
x: 100
|
|
415
|
-
y: 200
|
|
416
|
-
scale: 1.2
|
|
417
|
-
---
|
|
250
|
+
```kotlin
|
|
251
|
+
println(userName)
|
|
418
252
|
```
|
|
419
253
|
|
|
420
|
-
|
|
254
|
+
</InlineCompilerError>
|
|
255
|
+
````
|
|
256
|
+
|
|
257
|
+
The props of
|
|
258
|
+
[`InlineCompilerError`](components/README.md#inlinecompilererror),
|
|
259
|
+
[`SmartCast`](components/README.md#smartcast),
|
|
260
|
+
[`Warning`](components/README.md#warning),
|
|
261
|
+
[`TypeHint`](components/README.md#typehint),
|
|
262
|
+
[`InlineValue`](components/README.md#inlinevalue), and
|
|
263
|
+
[`SuspendGutter`](components/README.md#suspendgutter) are in
|
|
264
|
+
[`components/README.md`](components/README.md).
|
|
265
|
+
|
|
266
|
+
## Drawn annotations
|
|
421
267
|
|
|
422
|
-
`<DrawnAnnotation>`
|
|
268
|
+
`<DrawnAnnotation>` draws a hand-drawn mark on an element or on exact text
|
|
269
|
+
inside a code block, and can connect it to a label or to another element:
|
|
423
270
|
|
|
424
271
|
````md
|
|
425
272
|
<DrawnAnnotation type="circle" text="fun main" label="program entry point" :on="1">
|
|
@@ -431,433 +278,349 @@ fun main() = println("Hello, Kotlin!")
|
|
|
431
278
|
</DrawnAnnotation>
|
|
432
279
|
````
|
|
433
280
|
|
|
434
|
-
|
|
281
|
+
- Marks: `underline`, `circle`, `box`, `strike-through`.
|
|
282
|
+
- Labels take a little Markdown: `` `code` ``, `**strong**`, `*emphasis*`,
|
|
283
|
+
`>` quotes, and line breaks.
|
|
284
|
+
- It follows Magic Move steps and Slidev's clicks.
|
|
435
285
|
|
|
436
|
-
|
|
286
|
+
Every prop, the click timing, and troubleshooting are in
|
|
287
|
+
[`components/README.md`](components/README.md#drawnannotation).
|
|
437
288
|
|
|
438
|
-
|
|
289
|
+
### Visual editor
|
|
439
290
|
|
|
440
|
-
|
|
441
|
-
|
|
291
|
+
Drag labels and connectors into place while the deck runs, instead of typing
|
|
292
|
+
coordinates. Turn it on in the deck's `vite.config.ts`:
|
|
442
293
|
|
|
443
|
-
```
|
|
444
|
-
|
|
294
|
+
```ts
|
|
295
|
+
import { defineConfig } from 'vite'
|
|
296
|
+
import { drawnAnnotationEditor } from 'slidev-theme-kotlin/annotation-editor'
|
|
297
|
+
|
|
298
|
+
export default defineConfig({
|
|
299
|
+
plugins: [drawnAnnotationEditor()],
|
|
300
|
+
})
|
|
445
301
|
```
|
|
446
302
|
|
|
447
|
-
|
|
448
|
-
````
|
|
303
|
+

|
|
449
304
|
|
|
450
|
-
|
|
305
|
+
Then, in `slidev dev`:
|
|
451
306
|
|
|
452
|
-
|
|
307
|
+
1. Press **Alt+Shift+A** (**⌥⇧A** on a Mac) on the slide you want to edit. Navigation pauses
|
|
308
|
+
until you leave the editor.
|
|
309
|
+
2. Drag a label, its width handle, or a connector. The arrow keys nudge the
|
|
310
|
+
selected control (**Shift** for larger steps).
|
|
311
|
+
3. Press **Done editing annotations**. The positions are saved as a
|
|
312
|
+
`:geometry` attribute on the tags you edited.
|
|
453
313
|
|
|
454
|
-
|
|
314
|
+
**Esc** cancels: first a drag, then the selection, then the whole session.
|
|
315
|
+
The toolbar can also hide, curve, or reset a connector. Nothing of the editor
|
|
316
|
+
is part of a built or exported deck. The details are in
|
|
317
|
+
[Visual-editor geometry](components/README.md#visual-editor-geometry).
|
|
455
318
|
|
|
456
|
-
|
|
457
|
-
# .editorconfig
|
|
458
|
-
[*.md]
|
|
459
|
-
ij_formatter_tags_enabled = true
|
|
460
|
-
```
|
|
319
|
+
### When an annotation finds nothing
|
|
461
320
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
321
|
+
In `slidev dev`, a red chip in the top-left corner of the slide tells you
|
|
322
|
+
that an annotation or decoration could not find its text or element. Hover it
|
|
323
|
+
for the message; the console prints the same. See
|
|
324
|
+
[When nothing appears](components/README.md#when-nothing-appears).
|
|
466
325
|
|
|
467
|
-
|
|
468
|
-
```
|
|
326
|
+
## Kodee
|
|
469
327
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
## Static handout, `llms.txt`, and hash-mode links
|
|
473
|
-
|
|
474
|
-
A Slidev build is a single-page app: without JavaScript its `index.html`
|
|
475
|
-
contains nothing but the title. Search engines render JavaScript for the root
|
|
476
|
-
URL at best, and AI crawlers (GPTBot, ClaudeBot, CCBot, …) render none, so the
|
|
477
|
-
code on your slides never reaches them. Every `slidev build` on this theme
|
|
478
|
-
therefore also writes, next to the app:
|
|
479
|
-
|
|
480
|
-
- `handout/index.html` and `handout/<section>.html` — one page per source
|
|
481
|
-
file (`src:` lesson), with every slide's title, prose, and highlighted code,
|
|
482
|
-
each linking back to the live slide. A single-file deck gets one page.
|
|
483
|
-
- `llms.txt` and `llms-full.txt` — the [llmstxt.org](https://llmstxt.org)
|
|
484
|
-
convention that coding agents and AI crawlers look for: an index, and the
|
|
485
|
-
full Markdown of the deck with complete code snippets (folded imports
|
|
486
|
-
expanded).
|
|
487
|
-
- `sitemap.xml` — only when the deck's public URL is known.
|
|
488
|
-
- `robots.txt` — only when that URL is the root of its host and `public/`
|
|
489
|
-
ships none. Crawlers read `robots.txt` at the host root alone, so a deck
|
|
490
|
-
served from a sub-path (a GitHub project page, a folder of your blog) gets
|
|
491
|
-
no file; add `Sitemap: <siteUrl>sitemap.xml` to the host's own
|
|
492
|
-
`robots.txt` instead, as the build reminds you.
|
|
493
|
-
|
|
494
|
-
The handout and `llms-full.txt` form one outline: the deck, then each
|
|
495
|
-
section, then each slide, with a slide's own headings placed below its
|
|
496
|
-
title. The handout pages carry a description, canonical URL, Open Graph
|
|
497
|
-
tags, and the deck's `lang` from the headmatter (`en` when unset).
|
|
498
|
-
|
|
499
|
-
The built `index.html` (and the `404.html` copied from it) is made
|
|
500
|
-
self-describing too: `rel="alternate"` links to the handout and
|
|
501
|
-
`llms-full.txt`, a plain-text `<meta name="description">` from the headmatter
|
|
502
|
-
`info` (replacing the one Slidev renders from the raw Markdown), and a short
|
|
503
|
-
paragraph linking to the same files as visible text, hidden as soon as the
|
|
504
|
-
app has mounted. That paragraph is ordinary markup rather than `<noscript>`,
|
|
505
|
-
which many text extractors drop, so a crawler that renders no JavaScript
|
|
506
|
-
still finds the content from the root URL. With `siteUrl` set the page also
|
|
507
|
-
carries the canonical URL and a `rel="sitemap"` link.
|
|
508
|
-
|
|
509
|
-
Theme markup is translated, not dropped: a `DrawnAnnotation` with a `label`
|
|
510
|
-
becomes a quoted note about the code it points at, an `InlineCompilerError`
|
|
511
|
-
becomes its diagnostic, Magic Move blocks become their steps, and
|
|
512
|
-
`<<< @/file#region` snippet imports are inlined.
|
|
513
|
-
|
|
514
|
-
Give the deck its public URL so links, the sitemap, and `llms.txt` are
|
|
515
|
-
absolute, and opt out of notes or the whole feature if you need to:
|
|
328
|
+
Turn the mascot on once for the whole deck:
|
|
516
329
|
|
|
517
330
|
```yaml
|
|
331
|
+
---
|
|
332
|
+
theme: kotlin
|
|
518
333
|
themeConfig:
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
notes: false # leave presenter notes out (default: included)
|
|
522
|
-
# handout: false # disable the static content entirely
|
|
334
|
+
kodee: greeting
|
|
335
|
+
---
|
|
523
336
|
```
|
|
524
337
|
|
|
525
|
-
|
|
338
|
+
Kodee sits small in the bottom-right corner of regular slides, and large
|
|
339
|
+
beside the title on `cover` and `intro` slides. Between slides it glides to
|
|
340
|
+
its new place.
|
|
526
341
|
|
|
527
|
-
|
|
528
|
-
which sets no cookies and stores no personal data, so it needs no consent
|
|
529
|
-
banner. Give the headmatter the endpoint of your GoatCounter site, or just
|
|
530
|
-
its code:
|
|
342
|
+
Change the expression for one slide, or hide the mascot:
|
|
531
343
|
|
|
532
344
|
```yaml
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
345
|
+
---
|
|
346
|
+
kodee: wink # or: kodee: false
|
|
347
|
+
---
|
|
536
348
|
```
|
|
537
349
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
title, so the dashboard's path list doubles as a per-slide drop-off curve.
|
|
541
|
-
Clicks within a slide, the presenter and overview views, notes, print, and
|
|
542
|
-
export are never counted. Handout pages carry the tag too and count under
|
|
543
|
-
their own URL. `slidev dev`, exports, and screenshots send nothing:
|
|
544
|
-
`count.js` ignores localhost and headless browsers by itself.
|
|
545
|
-
|
|
546
|
-
Every deck on one host can share one GoatCounter site, since paths carry the
|
|
547
|
-
deck's base. Add `#toggle-goatcounter` to a deck's URL to stop counting your
|
|
548
|
-
own visits in that browser.
|
|
549
|
-
|
|
550
|
-
## Compiling the snippets
|
|
350
|
+
The variants are `greeting`, `wink`, `wave`, `jumping`, `sitting`,
|
|
351
|
+
`drinking`, `heart`, `in-love`, `welcome`, `winter`, and `tiny`.
|
|
551
352
|
|
|
552
|
-
|
|
553
|
-
see above) but no package, and it may mix declarations with bare statements.
|
|
554
|
-
`slidev-kotlin-snippets` turns Kotlin and Java fences into source files, in the
|
|
555
|
-
spirit of [KotlinX Knit](https://github.com/Kotlin/kotlinx-knit), so a Gradle
|
|
556
|
-
module can compile the deck's code against the real libraries and a stale slide
|
|
557
|
-
fails the build instead of the talk.
|
|
353
|
+
### Size and position
|
|
558
354
|
|
|
559
|
-
|
|
355
|
+
Use the object form for full control:
|
|
560
356
|
|
|
561
357
|
```yaml
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
- presentation.support.*
|
|
569
|
-
```
|
|
570
|
-
|
|
571
|
-
```bash
|
|
572
|
-
npx slidev-kotlin-snippets # writes the files, removes stale ones
|
|
573
|
-
npx slidev-kotlin-snippets --check # exits 1 when they are out of date (CI)
|
|
574
|
-
npx slidev-kotlin-snippets talk.md # another entry than slides.md
|
|
358
|
+
---
|
|
359
|
+
kodee:
|
|
360
|
+
variant: greeting
|
|
361
|
+
size: large
|
|
362
|
+
position: featured
|
|
363
|
+
---
|
|
575
364
|
```
|
|
576
365
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
- the fence's imports come first, after the `imports` from the config. Keep
|
|
585
|
-
the tables, types, and values the slides use without defining them in a
|
|
586
|
-
hand-written package and star-import it; a fence that defines its own
|
|
587
|
-
`Talks` shadows the shared one, because a declaration in the file's own
|
|
588
|
-
package wins over a star import.
|
|
589
|
-
- Kotlin top-level declarations (`fun`, `val`, `object`, `class`, ... with
|
|
590
|
-
their annotations, modifiers, and `context(...)` lines) stay top-level, and
|
|
591
|
-
bare statements such as `Talks.selectAll()` are wrapped in `suspend fun main()`.
|
|
592
|
-
A Kotlin `package` line is dropped; `@file:` annotations move above the package.
|
|
593
|
-
- Java fences are emitted as Java compilation units with the generated package
|
|
594
|
-
and imports; they must therefore contain declarations rather than bare
|
|
595
|
-
statements. Their own `package` line is replaced. A fence declaring a public
|
|
596
|
-
top-level Java type is named after that type (as Java requires); otherwise it
|
|
597
|
-
uses the slide-based filename. When a later slide of the same section declares
|
|
598
|
-
a public type an earlier slide already declared, its file goes in a directory
|
|
599
|
-
named after the slide, so both compile.
|
|
600
|
-
- a slide with several fences in the same language tells them apart by their
|
|
601
|
-
first meta word (`jdbc`, `r2dbc`) or a letter.
|
|
602
|
-
|
|
603
|
-
Not every fence is meant to compile. Add `no-compile` to a fence that sketches
|
|
604
|
-
library internals or an intermediate step (`` ```kotlin no-compile ``); the flag
|
|
605
|
-
is stripped before Slidev sees it. Fences inside an `InlineCompilerError` are
|
|
606
|
-
skipped automatically, since the slide shows the diagnostic on purpose, and so
|
|
607
|
-
are `gradle` build-script fences.
|
|
608
|
-
|
|
609
|
-
Wire it into Gradle so `./gradlew check` also fails on stale sources:
|
|
366
|
+
| Key | Values | Default |
|
|
367
|
+
| --- | --- | --- |
|
|
368
|
+
| `variant` | one of the variants above | the deck's `themeConfig.kodee` |
|
|
369
|
+
| `size` | `small` (200px), `medium` (320px), `large` (600px; 500px for `wave`) | `small`; `large` on `cover` and `intro` |
|
|
370
|
+
| `position` | `corner`, `featured`, `custom` | `corner`; `featured` on `cover` and `intro` |
|
|
371
|
+
| `x`, `y` | pixels, with `position: custom` | – |
|
|
372
|
+
| `scale` | a multiplier, `1.2` for a fifth larger | `1` |
|
|
610
373
|
|
|
611
|
-
|
|
612
|
-
val snippetsCheck by tasks.registering(Exec::class) {
|
|
613
|
-
commandLine("npx", "slidev-kotlin-snippets", "--check")
|
|
614
|
-
}
|
|
615
|
-
tasks.check { dependsOn(snippetsCheck) }
|
|
616
|
-
```
|
|
374
|
+
## Layouts
|
|
617
375
|
|
|
618
|
-
|
|
376
|
+
| Layout | Use |
|
|
377
|
+
| --- | --- |
|
|
378
|
+
| `default` | A regular slide |
|
|
379
|
+
| `cover` | The title slide, with the text beside a large Kodee |
|
|
380
|
+
| `intro` | A section or speaker introduction, same arrangement as `cover` |
|
|
619
381
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
382
|
+
```yaml
|
|
383
|
+
---
|
|
384
|
+
layout: cover
|
|
385
|
+
---
|
|
623
386
|
|
|
624
|
-
|
|
625
|
-
slidev build --base /my-deck/ --router-mode hash
|
|
387
|
+
# Presentation Title
|
|
626
388
|
```
|
|
627
389
|
|
|
628
|
-
|
|
629
|
-
script to hash-mode builds that forwards old `/my-deck/12` links (and
|
|
630
|
-
`presenter/…`, `overview`, `notes`, `print`, `export`) to their hash
|
|
631
|
-
equivalent, so links shared before the switch keep working.
|
|
632
|
-
|
|
633
|
-
## Development
|
|
390
|
+
Slidev's built-in layouts, such as `two-cols-header`, keep working.
|
|
634
391
|
|
|
635
|
-
|
|
392
|
+
## Customising
|
|
636
393
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
npm run dev
|
|
394
|
+
Colours, sizes, and fonts are CSS variables. Override them in the deck's own
|
|
395
|
+
stylesheet (`style.css` or `styles/index.css`).
|
|
640
396
|
|
|
641
|
-
|
|
642
|
-
npm run build
|
|
397
|
+
### Code-window colours
|
|
643
398
|
|
|
644
|
-
|
|
645
|
-
npm run export
|
|
399
|
+
Each icon has a border colour, with a light and a dark value:
|
|
646
400
|
|
|
647
|
-
|
|
648
|
-
|
|
401
|
+
```css
|
|
402
|
+
:root {
|
|
403
|
+
--code-window-kotlin-color: #834df0;
|
|
404
|
+
--code-window-gradle-color: #6c707e;
|
|
405
|
+
--code-window-amper-color: #087cfa;
|
|
406
|
+
--code-window-arrow-color: #2455cf;
|
|
407
|
+
--code-window-ktor-color: #7f52ff;
|
|
408
|
+
--code-window-java-color: #e66d17;
|
|
409
|
+
--code-window-terminal-color: #6c707e;
|
|
410
|
+
--code-window-maven-color: #3574f0;
|
|
411
|
+
--code-window-postgresql-color: #336791;
|
|
412
|
+
--code-window-jdbc-color: #f59e0b;
|
|
413
|
+
--code-window-r2dbc-color: #06b6d4;
|
|
649
414
|
|
|
650
|
-
|
|
651
|
-
|
|
415
|
+
/* The `console` output panel. */
|
|
416
|
+
--code-window-output-background: #f7f8fa;
|
|
417
|
+
--code-window-output-color: #2b2d30;
|
|
418
|
+
}
|
|
652
419
|
|
|
653
|
-
|
|
654
|
-
|
|
420
|
+
html.dark {
|
|
421
|
+
--code-window-kotlin-color: #a571e6;
|
|
422
|
+
--code-window-output-background: #1e1f22;
|
|
423
|
+
--code-window-output-color: #bcbec4;
|
|
424
|
+
/* Override any of the other dark-mode values as needed. */
|
|
425
|
+
}
|
|
655
426
|
```
|
|
656
427
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
`npm run export:slide` is an AI-agent-friendly wrapper around `slidev export`. It writes one PNG rather than a directory of all exported pages:
|
|
428
|
+
The editor surface reads `--editor-bg`, `--editor-fg`, `--editor-border`, and
|
|
429
|
+
`--editor-shadow`. `--code-window-border` overrides the border of one window.
|
|
660
430
|
|
|
661
|
-
|
|
662
|
-
# Initial state of slide 12
|
|
663
|
-
npm run export:slide -- --slide 12
|
|
431
|
+
### Code size
|
|
664
432
|
|
|
665
|
-
|
|
666
|
-
|
|
433
|
+
Every code window uses one font size, chosen so that a full-width window fits
|
|
434
|
+
80 characters. Change the number of columns for the deck, or for one slide
|
|
435
|
+
class:
|
|
667
436
|
|
|
668
|
-
|
|
669
|
-
|
|
437
|
+
```css
|
|
438
|
+
:root {
|
|
439
|
+
--code-window-columns: 72;
|
|
440
|
+
}
|
|
670
441
|
```
|
|
671
442
|
|
|
672
|
-
|
|
443
|
+
| Variable | Default | Use |
|
|
444
|
+
| --- | --- | --- |
|
|
445
|
+
| `--code-window-columns` | `80` | Characters that fit a full-width window |
|
|
446
|
+
| `--code-window-font-size` | from the columns | Set a size such as `1.25rem` and skip the calculation |
|
|
447
|
+
| `--code-window-inline-space` | the standard layout's | Horizontal padding, for a custom layout |
|
|
448
|
+
| `--code-window-character-width` | `0.61` | Width of one character relative to the font size; change it for another monospace font |
|
|
449
|
+
| `--code-window-line-height` | `1.42` | Line height |
|
|
673
450
|
|
|
674
|
-
###
|
|
451
|
+
### Cover text width
|
|
675
452
|
|
|
676
|
-
|
|
453
|
+
On `cover` and `intro` slides the text sits in a column beside Kodee. The
|
|
454
|
+
column is `--cover-text-width`, 48% by default. Widen it when the titles are
|
|
455
|
+
short, or give a slide without the mascot the full width:
|
|
677
456
|
|
|
678
|
-
```
|
|
679
|
-
|
|
457
|
+
```css
|
|
458
|
+
.slidev-layout.cover.no-mascot {
|
|
459
|
+
--cover-text-width: 100%;
|
|
460
|
+
}
|
|
680
461
|
```
|
|
681
462
|
|
|
682
|
-
|
|
463
|
+
### Fonts
|
|
683
464
|
|
|
684
|
-
|
|
465
|
+
The theme uses JetBrains Sans and JetBrains Mono when they are installed on
|
|
466
|
+
your machine. Otherwise it falls back to Inter and to JetBrains Mono from
|
|
467
|
+
Google Fonts.
|
|
685
468
|
|
|
686
|
-
|
|
687
|
-
.
|
|
688
|
-
├── components/ # Vue components
|
|
689
|
-
│ ├── DrawnAnnotation.vue # Hand-drawn marks, labels, and connectors
|
|
690
|
-
│ ├── DrawnAnnotationEditor*.vue # Development-only editor launcher and toolbar
|
|
691
|
-
│ ├── DrawnAnnotationProblems.vue # Development-only problem chips
|
|
692
|
-
│ ├── drawn-annotation/ # Editor store, geometry, and browser writer
|
|
693
|
-
│ ├── InlineCompilerError.vue # Inline code diagnostics
|
|
694
|
-
│ ├── SmartCast.vue # Smart-cast background behind exact code
|
|
695
|
-
│ ├── Warning.vue # Warning background with an optional message
|
|
696
|
-
│ ├── TypeHint.vue # Inlay hint after a lambda's brace
|
|
697
|
-
│ ├── InlineValue.vue # Debugger inline value after a line
|
|
698
|
-
│ ├── code-highlight.ts # The mechanism the four above share
|
|
699
|
-
│ ├── code-text-match.ts # Text search shared with DrawnAnnotation
|
|
700
|
-
│ ├── MagicMoveBetween.vue # Cross-slide Magic Move code window
|
|
701
|
-
│ ├── Kodee.vue # Main Kodee mascot component with animations
|
|
702
|
-
│ └── KodeeWrapper.vue # Wrapper component for Kodee integration
|
|
703
|
-
├── global-top.vue # Development-only editor toolbar and problem chips
|
|
704
|
-
├── global-bottom.vue # Deck-wide Kodee layer that follows the current slide
|
|
705
|
-
├── slide-bottom.vue # Per-slide Kodee for the overview and presenter preview
|
|
706
|
-
├── plugins/ # Development-only source writer (`slidev-theme-kotlin/annotation-editor`)
|
|
707
|
-
├── bin/ # `slidev-kotlin-snippets` CLI
|
|
708
|
-
├── layouts/ # Slidev layout templates
|
|
709
|
-
│ ├── cover.vue # Cover slide layout
|
|
710
|
-
│ ├── default.vue # Default slide layout
|
|
711
|
-
│ └── intro.vue # Introduction slide layout
|
|
712
|
-
├── assets/ # Bundled theme assets
|
|
713
|
-
│ ├── kodee-*.svg # Kodee mascot variants (11 variants)
|
|
714
|
-
│ └── code-window/ # Code-window identity icons, referenced from layout.css
|
|
715
|
-
├── index.html # Head tags merged into every deck (font preconnect)
|
|
716
|
-
├── setup/ # Slidev setup files
|
|
717
|
-
│ ├── main.ts # App setup: per-slide analytics pageviews
|
|
718
|
-
│ ├── preparser.ts # The bare `magic-move` slide separator
|
|
719
|
-
│ ├── shiki.ts # Syntax highlighting configuration for Kotlin
|
|
720
|
-
│ ├── transformers.ts # Cross-slide Magic Move and import folding passes
|
|
721
|
-
│ ├── magic-move-between.ts # Precompiled Magic Move steps
|
|
722
|
-
│ ├── analytics.ts # GoatCounter tag and per-slide pageviews
|
|
723
|
-
│ ├── code-imports.ts # Folded imports: fence scanning and source edits
|
|
724
|
-
│ ├── kotlin-snippets.ts # Compilable snippet files per fence
|
|
725
|
-
│ ├── static-content.ts # Handout, llms.txt, sitemap generation
|
|
726
|
-
│ ├── hash-route-redirect.ts # Old history-mode links → hash routes
|
|
727
|
-
│ └── vite-plugins.ts # Registers the three build plugins above
|
|
728
|
-
├── styles/ # Theme styles
|
|
729
|
-
│ ├── index.ts # Style entry point
|
|
730
|
-
│ └── layout.css # Layout styles
|
|
731
|
-
├── .pi/extensions/inspect-shiki-token-scopes.ts # Local Pi tool for inspecting Shiki token scopes
|
|
732
|
-
├── slides.md # Example presentation used for local development and GitHub Pages
|
|
733
|
-
├── package.json # Package configuration and dependencies
|
|
734
|
-
└── LICENSE # Apache License 2.0
|
|
735
|
-
```
|
|
469
|
+
### `themeConfig` reference
|
|
736
470
|
|
|
737
|
-
|
|
471
|
+
| Key | Default | Use |
|
|
472
|
+
| --- | --- | --- |
|
|
473
|
+
| `kodee` | off | The deck's [Kodee](#kodee) variant, or the object form |
|
|
474
|
+
| `foldImports` | `true` | `false` keeps every import visible |
|
|
475
|
+
| `drawnAnnotation.connect` | `true` | `false` draws labels without a line to their mark |
|
|
476
|
+
| `siteUrl` | – | The deck's public URL, for [publishing](#publishing-a-deck) |
|
|
477
|
+
| `handout` | on | `false` turns the static handout off; `handout.notes: false` leaves presenter notes out |
|
|
478
|
+
| `analytics.goatcounter` | – | A GoatCounter site, to count pageviews |
|
|
479
|
+
| `snippets` | – | Where [compiled snippets](#compiling-the-snippets) go |
|
|
738
480
|
|
|
739
|
-
|
|
481
|
+
## Publishing a deck
|
|
740
482
|
|
|
741
|
-
|
|
483
|
+
A built Slidev deck is a JavaScript app, so search engines and AI crawlers
|
|
484
|
+
see none of your code. Every `slidev build` on this theme therefore also
|
|
485
|
+
writes a static **handout** (`handout/index.html`), **`llms.txt`** and
|
|
486
|
+
**`llms-full.txt`**, and a sitemap. Give the deck its public URL so the links
|
|
487
|
+
are absolute:
|
|
742
488
|
|
|
743
|
-
|
|
489
|
+
```yaml
|
|
490
|
+
themeConfig:
|
|
491
|
+
siteUrl: https://example.github.io/my-deck/
|
|
492
|
+
```
|
|
744
493
|
|
|
745
|
-
|
|
746
|
-
capture every settled click state from Slidev's normal presenter/player route
|
|
747
|
-
(`/<slide>?clicks=<click>`), rather than the print-export `?print=clicks` route.
|
|
748
|
-
It renders the current working tree and a Git revision with the same browser,
|
|
749
|
-
compares every PNG with `pixelmatch`, and writes a three-column
|
|
750
|
-
base/current/diff page. When the local
|
|
751
|
-
`../kotlin-fundamentals` consumer deck is available, its baseline is
|
|
752
|
-
rendered from its clean `HEAD` and its current capture includes its working-tree
|
|
753
|
-
changes, with each capture linked to the corresponding theme revision.
|
|
754
|
-
Screenshots are not committed: the chosen Git revision is the baseline, so
|
|
755
|
-
accepting an intentional visual change is simply reviewing it and committing
|
|
756
|
-
that change.
|
|
494
|
+
On GitHub Pages, build with the hash router so deep links resolve:
|
|
757
495
|
|
|
758
496
|
```bash
|
|
759
|
-
|
|
760
|
-
|
|
497
|
+
slidev build --base /my-deck/ --router-mode hash
|
|
498
|
+
```
|
|
761
499
|
|
|
762
|
-
|
|
763
|
-
|
|
500
|
+
To count pageviews per slide without cookies, add a
|
|
501
|
+
[GoatCounter](https://www.goatcounter.com) site:
|
|
764
502
|
|
|
765
|
-
|
|
766
|
-
|
|
503
|
+
```yaml
|
|
504
|
+
themeConfig:
|
|
505
|
+
analytics:
|
|
506
|
+
goatcounter: mysite
|
|
767
507
|
```
|
|
768
508
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
509
|
+
What each file contains, `robots.txt`, and what is counted are in
|
|
510
|
+
[`docs/publishing.md`](docs/publishing.md).
|
|
511
|
+
|
|
512
|
+
## Presenting from your phone
|
|
773
513
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
package install scripts, download it once with:
|
|
514
|
+
`slidev-kotlin-present` serves a built deck on your local network and turns
|
|
515
|
+
your phone into the clicker. No internet connection is needed.
|
|
777
516
|
|
|
778
517
|
```bash
|
|
779
|
-
|
|
518
|
+
slidev build
|
|
519
|
+
npx slidev-kotlin-present
|
|
780
520
|
```
|
|
781
521
|
|
|
782
|
-
|
|
783
|
-
|
|
522
|
+
It prints a **Display** link and a QR code. Open the link on the laptop, scan
|
|
523
|
+
the code with your phone, and tap: the right half of the screen advances, the
|
|
524
|
+
left half goes back. The phone shows what the next tap reveals, your speaker
|
|
525
|
+
notes, and whether the display follows.
|
|
526
|
+
|
|
527
|
+
Only a page opened from the QR code can move the slides. The laptop and the
|
|
528
|
+
phone must be on the same network; when the venue's Wi-Fi keeps them apart,
|
|
529
|
+
use your phone's hotspot.
|
|
784
530
|
|
|
785
|
-
|
|
531
|
+
On a laptop, `/presenter/1` is still Slidev's presenter console. The status
|
|
532
|
+
line, the options, and who can read what are in
|
|
533
|
+
[`docs/presenting.md`](docs/presenting.md).
|
|
786
534
|
|
|
787
|
-
|
|
535
|
+
## Compiling the snippets
|
|
788
536
|
|
|
789
|
-
|
|
537
|
+
`slidev-kotlin-snippets` turns the Kotlin and Java fences of a deck into
|
|
538
|
+
source files, so a Gradle module can compile them against the real libraries.
|
|
539
|
+
A stale slide then fails the build instead of the talk.
|
|
790
540
|
|
|
791
|
-
```
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
"fonts": {
|
|
797
|
-
"sans": "JetBrains Sans,Inter",
|
|
798
|
-
"mono": "JetBrains Mono Local,JetBrains Mono",
|
|
799
|
-
"local": "JetBrains Sans,JetBrains Mono Local",
|
|
800
|
-
"weights": "400,700,800",
|
|
801
|
-
"italic": true
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
}
|
|
541
|
+
```yaml
|
|
542
|
+
themeConfig:
|
|
543
|
+
snippets:
|
|
544
|
+
dir: src/main/kotlin/snippets
|
|
545
|
+
package: snippets
|
|
806
546
|
```
|
|
807
547
|
|
|
808
|
-
|
|
548
|
+
```bash
|
|
549
|
+
npx slidev-kotlin-snippets # writes the files, removes stale ones
|
|
550
|
+
npx slidev-kotlin-snippets --check # exits 1 when they are out of date (CI)
|
|
551
|
+
```
|
|
809
552
|
|
|
810
|
-
|
|
553
|
+
Add `no-compile` to a fence that is not meant to compile
|
|
554
|
+
(`` ```kotlin no-compile ``). The Gradle wiring, the remaining options, and
|
|
555
|
+
how fences map to files are in [`docs/snippets.md`](docs/snippets.md).
|
|
811
556
|
|
|
812
|
-
|
|
813
|
-
2. **cover** - Cover slide for presentation title
|
|
814
|
-
3. **intro** - Introduction slide layout
|
|
557
|
+
## Tips
|
|
815
558
|
|
|
816
|
-
|
|
559
|
+
### IntelliJ's Reformat Code
|
|
817
560
|
|
|
818
|
-
|
|
561
|
+
IntelliJ treats only the first YAML block of a Markdown file as frontmatter,
|
|
562
|
+
so **Reformat Code** breaks the frontmatter of later slides. Enable formatter
|
|
563
|
+
tags and switch formatting off after the headmatter:
|
|
564
|
+
|
|
565
|
+
```ini
|
|
566
|
+
# .editorconfig
|
|
567
|
+
[*.md]
|
|
568
|
+
ij_formatter_tags_enabled = true
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
```md
|
|
819
572
|
---
|
|
820
|
-
|
|
573
|
+
theme: kotlin
|
|
821
574
|
---
|
|
822
575
|
|
|
823
|
-
|
|
576
|
+
<!-- @formatter:off -->
|
|
824
577
|
```
|
|
825
578
|
|
|
826
|
-
##
|
|
579
|
+
## Developing the theme
|
|
580
|
+
|
|
581
|
+
`slides.md` is the demo deck. `slides.fixtures.md` holds edge cases for the
|
|
582
|
+
visual review and is not published.
|
|
827
583
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
584
|
+
| Command | What it does |
|
|
585
|
+
| --- | --- |
|
|
586
|
+
| `npm run dev` | Serve the demo deck with hot reload |
|
|
587
|
+
| `npm run build` | Lint and build the demo deck |
|
|
588
|
+
| `npm test` | Unit tests |
|
|
589
|
+
| `npm run test:annotation-editor` | Browser test of the visual editor |
|
|
590
|
+
| `npm run test:remote` | Browser test of the phone remote, after `npm run build` |
|
|
591
|
+
| `npm run present` | Present the built demo deck with the phone remote |
|
|
592
|
+
| `npm run visual:review` | Compare screenshots of the working tree with the last commit |
|
|
593
|
+
| `npm run export` | Export the demo deck to PDF |
|
|
594
|
+
| `npm run screenshot` | Export every slide and click state as PNG |
|
|
595
|
+
| `npm run export:slide -- --slide 12 --click 3` | Export one slide as PNG; `--click` starts at 0 |
|
|
835
596
|
|
|
836
|
-
###
|
|
837
|
-
- `@slidev/cli` (52.19.1) - Slidev command-line interface
|
|
597
|
+
### Visual review
|
|
838
598
|
|
|
839
|
-
|
|
599
|
+
`npm run visual:review` renders every click state of both decks twice, for
|
|
600
|
+
the working tree and for a Git revision, and writes a page with base,
|
|
601
|
+
current, and diff side by side. Open the `file://` path it prints.
|
|
840
602
|
|
|
841
|
-
|
|
603
|
+
```bash
|
|
604
|
+
npm run visual:review # against the last commit
|
|
605
|
+
npm run visual:review -- --base origin/main # against a branch
|
|
606
|
+
npm run visual:check -- --base origin/main # fail on any difference
|
|
607
|
+
```
|
|
842
608
|
|
|
843
|
-
|
|
609
|
+
No screenshots are committed: the Git revision is the baseline, so you accept
|
|
610
|
+
a visual change by committing it. If Chromium is missing, install it once
|
|
611
|
+
with `npx playwright install chromium`.
|
|
844
612
|
|
|
845
613
|
## License
|
|
846
614
|
|
|
847
|
-
|
|
615
|
+
Apache License 2.0. See [LICENSE](LICENSE).
|
|
848
616
|
|
|
849
617
|
## Credits
|
|
850
618
|
|
|
851
|
-
- **Kodee
|
|
852
|
-
- **
|
|
853
|
-
- **Slidev**: Presentation framework by [Anthony Fu](https://github.com/antfu)
|
|
619
|
+
- **Kodee** and the Kotlin logo: JetBrains
|
|
620
|
+
- **Slidev**: [Anthony Fu](https://github.com/antfu)
|
|
854
621
|
|
|
855
622
|
## Links
|
|
856
623
|
|
|
857
|
-
- [Slidev
|
|
858
|
-
- [Kotlin
|
|
624
|
+
- [Slidev documentation](https://sli.dev)
|
|
625
|
+
- [Kotlin](https://kotlinlang.org)
|
|
859
626
|
- [Package on npm](https://www.npmjs.com/package/slidev-theme-kotlin)
|
|
860
|
-
|
|
861
|
-
---
|
|
862
|
-
|
|
863
|
-
Made with ❤️ for the Kotlin community
|