slidev-theme-kotlin 0.12.1 → 0.13.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.
Files changed (66) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +350 -27
  3. package/{public → assets/code-window}/Postgresql_elephant.svg +21 -21
  4. package/assets/code-window/run.svg +3 -0
  5. package/bin/kotlin-snippets.mjs +63 -0
  6. package/components/DrawnAnnotation.vue +658 -246
  7. package/components/DrawnAnnotationEditorLauncher.vue +11 -2
  8. package/components/DrawnAnnotationEditorToolbar.vue +284 -87
  9. package/components/DrawnAnnotationProblems.vue +87 -0
  10. package/components/InlineCompilerError.vue +240 -270
  11. package/components/InlineValue.vue +55 -0
  12. package/components/Kodee.vue +8 -17
  13. package/components/KodeeWrapper.vue +22 -35
  14. package/components/MagicMoveBetween.vue +98 -24
  15. package/components/README.md +376 -40
  16. package/components/SmartCast.vue +50 -0
  17. package/components/TypeHint.vue +75 -0
  18. package/components/Warning.vue +50 -0
  19. package/components/annotation-label-markdown.ts +139 -22
  20. package/components/annotation-line-breaks.ts +7 -0
  21. package/components/code-highlight.ts +434 -0
  22. package/components/code-text-match.ts +205 -24
  23. package/components/drawn-annotation/editor-store.ts +92 -13
  24. package/components/drawn-annotation/geometry.ts +40 -2
  25. package/components/drawn-annotation/writer-client.ts +8 -10
  26. package/components/kodee-props.ts +44 -0
  27. package/components/kodee-view-transition.ts +13 -0
  28. package/components/magic-move/batched-renderer.ts +158 -0
  29. package/components/transition-ink.ts +15 -10
  30. package/global-bottom.vue +48 -0
  31. package/global-top.vue +8 -3
  32. package/index.html +9 -0
  33. package/package.json +12 -6
  34. package/plugins/drawn-annotation-editor.d.ts +2 -1
  35. package/plugins/drawn-annotation-editor.mjs +117 -30
  36. package/setup/analytics.ts +133 -0
  37. package/setup/code-imports.ts +297 -0
  38. package/setup/code-window-icon.ts +2 -0
  39. package/setup/hash-route-redirect.ts +46 -0
  40. package/setup/kotlin-snippets.ts +510 -0
  41. package/setup/magic-move-between.ts +33 -11
  42. package/setup/main.ts +28 -0
  43. package/setup/shiki.ts +29 -1
  44. package/setup/static-content.ts +867 -0
  45. package/setup/transformers.ts +67 -17
  46. package/setup/vite-plugins.ts +20 -0
  47. package/styles/layout.css +76 -16
  48. package/components/annotation-label-markdown.test.ts +0 -22
  49. package/components/drawn-annotation/editor-store.test.ts +0 -141
  50. package/components/drawn-annotation/geometry.test.ts +0 -88
  51. package/components/transition-ink.test.ts +0 -26
  52. package/plugins/drawn-annotation-editor.test.ts +0 -386
  53. package/public/kotlin-logo-name-dark-mode.svg +0 -45
  54. package/public/kotlin-logo-name-light-mode.svg +0 -44
  55. package/public/kotlin-logo.svg +0 -19
  56. /package/{public → assets/code-window}/amper-icon.svg +0 -0
  57. /package/{public → assets/code-window}/gradle.svg +0 -0
  58. /package/{public → assets/code-window}/gradle_dark.svg +0 -0
  59. /package/{public → assets/code-window}/java.svg +0 -0
  60. /package/{public → assets/code-window}/java_dark.svg +0 -0
  61. /package/{public → assets/code-window}/kotlin.svg +0 -0
  62. /package/{public → assets/code-window}/kotlin_dark.svg +0 -0
  63. /package/{public → assets/code-window}/maven.svg +0 -0
  64. /package/{public → assets/code-window}/maven_dark.svg +0 -0
  65. /package/{public → assets/code-window}/terminal.svg +0 -0
  66. /package/{public → assets/code-window}/terminal_dark.svg +0 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,84 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ### Removed
6
+
7
+ - The three Kotlin logo files under `public/`: nothing in the theme referenced them, and the README no longer lists them as a feature.
8
+
9
+ ### Fixed
10
+
11
+ - The text on `cover` and `intro` slides no longer runs under the featured Kodee: the layouts keep it in a column of `--cover-text-width` (48% of the content box by default, clear of the mascot's arm) and wrap it there. The showcase's own cover subtitle, intro bullets, and closing title had been partly hidden behind the mascot since the deck was written. A deck widens the column from its stylesheet, or sets `100%` on the class of a slide that hides the mascot.
12
+ - `SmartCast` and `Warning` follow the colour scheme: in dark mode the token background is Darcula's smart-cast green (`#294436`) and warning olive (`#52503a`) instead of the IntelliJ Light tints, which left Darcula's grey code nearly unreadable on a pastel bar, and the messages brighten to `#7bc47f` and `#d4b44a`. The same variables (`--smart-cast-color`, `--warning-color`, and the `-message-color` pair) still override both schemes, as `TypeHint` already did.
13
+
14
+ ## 0.13.0
15
+
16
+ ### Added
17
+
18
+ - `InlineCompilerError` underlines a diagnostic that spans several lines: `\n` in `text` (or a real newline in a bound string) continues the match on the next source line, indentation included, every line of the match gets its own squiggle, and the message sits after the line the match starts on, as IntelliJ places its inline diagnostics. `line` then names the first line of the match. `SmartCast` and `DrawnAnnotation` accept the same `\n` in `text`.
19
+ - `DrawnAnnotation` takes `line`, like `InlineCompilerError` and `SmartCast`: the one-based source line of a code block to search `text` on, with `occurrence` counted on that line only. A repeated token is reached by naming its line instead of counting its occurrences. A slide with several code blocks numbers each from one, and a match never straddles two blocks.
20
+ - The text search the three components share lives in `code-text-match.ts`: one segmenter for static Shiki and Magic Move blocks that stamps every segment with its source line, line-scoped and multi-line matching, the trimmed range of a line, and the merge of a range's client rects into visual lines. `InlineCompilerError` and `SmartCast` no longer carry their own copies, and the module has unit tests.
21
+ - `Warning` paints IntelliJ's warning yellow (`#faf4d7`, `--warning-color`) behind an exact piece of code, for the `Array` property of a data class or a parameter nobody reads. It takes `SmartCast`'s props (`text`, `line`, `occurrence`, `at` / `until` / `on`) and follows Magic Move the same way. An optional `message` sits after the end of the match's first line, where `InlineCompilerError` puts its diagnostics, in `--warning-message-color` and `--warning-message-size`; `SmartCast` accepts the same `message`. The mechanism both share now lives in `code-highlight.ts`, and each component only names its class and colours. The Kotlin snippet CLI keeps generating a fence under `Warning`, unlike one under `InlineCompilerError`.
22
+ - `InlineCompilerError`, `Warning`, and `SmartCast` render `message` as the same safe Markdown as a `DrawnAnnotation` label: `` `Int` `` is inline code in the mono stack, `\n` breaks the line as before, and HTML stays escaped. `annotation-label-markdown.ts` now renders all four, and the label CSS moved from `DrawnAnnotation`'s scoped styles into the theme stylesheet so every component shares it.
23
+ - `TypeHint` draws IntelliJ's inlay hint for a lambda with receiver: the grey `this: HTML` pill after the `{` on `line`, for DSL slides. `text` defaults to `{`, `receiver` names the type, `context` instead renders a `context(...)` block's hint as `context(Clock)` without `this:`, and the click props are `SmartCast`'s. It is the shared `code-highlight.ts` mechanism with painting turned off (`highlightClass` is now optional), so the pill follows Magic Move the way a `Warning` message does and sits after the line's last token when code follows the brace. Colours and size come from `--type-hint-color`, `--type-hint-background`, and `--type-hint-size`, with Darcula values under `html.dark`.
24
+ - A ```` ```console ```` fence renders as program output instead of a second editor window: a flat panel in the colours of IntelliJ's Run tool window (`--code-window-output-background`, `--code-window-output-color`, with new-UI dark values under `html.dark`), without the editor's outline and shadow, marked with the green run icon (`run.svg`) where a Kotlin fence shows its logo. Shiki's shell-session colouring is neutralised so output is one colour. Decks that already put a `console` fence under their Kotlin fences get the panel without a slide change.
25
+ - `InlineValue` draws IntelliJ's debugger inline value: the grey `numbers: [1, 2, 3]` after the line named by `text` (and `line` / `occurrence`), for output slides where each line has its own result. `value` is shown as written, and the click props are `SmartCast`'s. It is the shared `code-highlight.ts` mechanism with painting turned off, like `TypeHint`, in the code font at the code's size; `--inline-value-color` and `--inline-value-size` restyle it.
26
+ - `themeConfig.drawnAnnotation.connect: false` hides every `DrawnAnnotation` leader line in a deck, for decks that place their labels by hand; a slide's `drawnAnnotation.connect` frontmatter overrides the deck and a tag's `:connect` binding overrides both. The editor's **Hide connector** / **Show connector** toggle knows that default: it writes `:connect="true"` or `:connect="false"` only where the wanted state differs from it, and removes the binding when it matches. The source writer accepts `connect: null` to remove the binding; it owns that binding next to `:geometry`, and a tag's locator ignores both. The routing and curve controls are disabled while the connector is hidden.
27
+ - Folded imports: a leading block of `import` lines in a Kotlin, Java, or Scala fence leaves the code window on the slide, on ordinary fences, cross-slide Magic Move chains, and classic Magic Move blocks alike. Line numbers, highlight ranges, and annotation targets count from the first line of real code. `show-imports` on a fence or `themeConfig.foldImports: false` keeps imports visible. A `// Example` comment line (optionally `// Example: description`) folds everything above it instead, helper declarations included; the marker line leaves the slide too. The handout, `llms-full.txt`, and generated Kotlin sources keep the fence complete.
28
+ - Static content on every `slidev build`: a crawlable `handout/` (one page per source file, with highlighted code, Open Graph tags, the deck's `lang`, and links back to each slide), `llms.txt` and `llms-full.txt`, plus `sitemap.xml` when `themeConfig.siteUrl` is set and `robots.txt` when that URL is the root of its host (elsewhere the build prints the `Sitemap:` line to add to the host's own `robots.txt`). Slide headings sit below their slide's title, so the handout and `llms-full.txt` read as one outline. The built `index.html` links to the handout and `llms-full.txt` (`rel="alternate"`, a paragraph of visible links that the app hides once mounted, a plain-text description in place of Slidev's, and the canonical URL and sitemap once `siteUrl` is known), so crawlers that render no JavaScript find the content from the root URL. `themeConfig.handout: false` disables it; `handout.notes: false` leaves presenter notes out.
29
+ - `slidev-kotlin-snippets`, a CLI that writes one compilable Kotlin file per Kotlin fence of a deck (in the spirit of KotlinX Knit): each file gets its own package, the fence's imports plus the `themeConfig.snippets.imports` a deck shares, top-level declarations as written, and bare statements wrapped in `suspend fun main()`. `--check` fails when the files are stale, for CI. `no-compile` on a fence skips it, as does an `InlineCompilerError` wrapper; the flag is stripped from the rendered fence like `show-imports`, whether or not imports fold. `@slidev/parser` and `jiti`, which the CLI runs on, are now dependencies of the theme.
30
+ - Java fences now generate compilable `.java` files under `themeConfig.snippets.javaDir` (default `src/main/java/snippets`). Java and Kotlin fences on the same slide use the same generated package so interop examples compile together. A later slide of the same section that declares a public type an earlier slide already declared (two interop slides each showing `public class Person`) gets a directory named after the slide, `Person.java` inside, instead of a numbered file javac rejects.
31
+ - GoatCounter analytics: `themeConfig.analytics.goatcounter` (an endpoint or a site code) adds `count.js` to the built deck and its handout pages, and the theme sends one pageview per slide (`<base>/<slide number>` with the slide's title) instead of one per load, skipping clicks, presenter, overview, notes, print, and export views.
32
+ - Hash-mode builds (`slidev build --router-mode hash`) get a script forwarding old history-mode deep links such as `/deck/12` to `/deck/#/12`, so links shared before the switch keep working on hosts without a single-page-app rewrite.
33
+ - Development-only problem chips: while Vite serves the deck, a `DrawnAnnotation` whose `text`, `selector`, `occurrence`, or `target` matches nothing, an `InlineCompilerError` whose diagnostic is not shown, and a `SmartCast`, `Warning`, `TypeHint`, or `InlineValue` whose `text` is not found, put a red chip in the top-left corner of their slide next to the console warning they already print. The chip follows the warning's timing (current slide, settled, click reached), withdraws when the target turns up, and never appears in builds, exports, the presenter view, or print mode. `InlineCompilerError`'s console warnings now gate on Vite's explicit development mode like `DrawnAnnotation`'s editor, instead of `import.meta.env.DEV`.
34
+ - Annotation editing is a frozen session: entering the editor locks Slidev navigation (shortcuts, click-to-navigate, nav controls, swipes) on the current slide and click, every drag and arrow-key nudge stays a local draft, and **Done editing annotations** commits all changed drafts in one write phase; a failed write keeps the drafts and the session. **Esc** cancels in stages (drag, selection, then the session after a confirmation).
35
+ - Quadratic connector editing: the first edit of an automatic leader keeps its visible curve as a persisted control point, which renders as a draggable, keyboard-nudgeable handle with a dashed control-polygon guide. **Curve connector** and **Straighten connector** add or remove the control point from the toolbar, and the connector's hit target follows the rendered curve.
36
+ - **Reset slide** in the editor toolbar removes the saved geometry of every annotation on the current slide, after a confirmation.
37
+
38
+ ### Fixed
39
+
40
+ - `DrawnAnnotation` settles only on the slide on screen when it mounts. Slidev mounts every slide of the deck, and a whole-show remount mounts them all again, so each annotation walked the document's running animations twice at mount time for slides nobody was looking at — on a deck with a few hundred annotations, that many document-wide walks per remount. Becoming current settles as before.
41
+ - `DrawnAnnotation` marks on text are trimmed to the font's ink, cap height to descender, instead of the line's full content area. JetBrains Mono reserves 1.32em per line while its glyphs span about 0.9em, so a mark drawn from the text rect floated away from the letters and, in a code block, into the neighbouring lines; automatically placed labels and routed leaders measure from the trimmed box too, so they sit a little closer to the code.
42
+ - A committed annotation draft is dropped once Slidev has recompiled the Markdown that holds it. The commit re-seeded the draft with what the writer saved, and nothing ever cleared it, so a later hand edit of the tag's `:geometry` binding stayed invisible in the browser and **Reload saved geometry** asked to discard changes that were already saved.
43
+ - The static handout and `llms-full.txt` translate `SmartCast`, `Warning`, `TypeHint`, and `InlineValue` into notes, as they already did `DrawnAnnotation` and `InlineCompilerError`. The four fell through to the generic tag strip, which cannot cross the `<` of a generic inside an attribute, so `<Warning text="names: List<String>">` left half a tag in the prose; a `message` or `value` was dropped either way.
44
+ - The JDBC and R2DBC code-window badges read `--font-sans`, which Slidev's UnoCSS preset never defines, and rendered in the browser's generic sans-serif; they read `--slidev-font-sans`. The slide's bottom rule hard-coded a purple one shade off the theme primary and now uses `--slidev-theme-primary`.
45
+ - A `console` fence inside a cross-slide Magic Move chain renders as the output panel like a static one; the panel's rules only matched the class on a static fence's `<code>`, not the one the chain transformer puts on the wrapper.
46
+ - `scripts/lint-slides.mjs` calls `@slidev/parser`'s `load` with its current signature and registers the theme's preparser, so a deck with root-absolute `src:` entries lints and chain slides' frontmatter is read as written; `scripts/visual-review.mjs` looks for the kotlin-fundamentals deck next to the theme instead of at an old path.
47
+ - `Kodee`'s unknown-variant warning gates on Vite's explicit development mode like every other development-only message.
48
+ - The npm tarball no longer ships the theme's test files.
49
+ - `Warning` and `SmartCast` no longer eat the last glyph of a token when a match spans several Shiki tokens: each painted token reached `0.15em` into its neighbour through a negative margin, and the later token's background painted over the earlier one's text (`println` read `printlr`, `String?` read `Strinq`). The highlight now sits flush with the text and square, as IntelliJ paints it, so adjacent tokens join into one bar.
50
+ - `InlineCompilerError` resolves its target once per DOM state instead of once per animation frame, the way `DrawnAnnotation` already did: while Magic Move moves retained tokens, or a resize or font load moves boxes, the range found before is reused and only its client rects are read again; a replaced child or edited text drops it. The search itself was already cheap (tens of microseconds on a deck-sized code window), so this removes work rather than a visible stutter — on the kotlin-fundamentals deck a Magic Move step spends about 25 ms of scripting in the annotation components over its whole transition, and the frame a key press drops is spent in Slidev's navigation and Shiki's per-token measurements, not in the text search.
51
+ - Kodee is drawn once for the whole slideshow, in a `global-bottom` layer that follows the current slide's `kodee` frontmatter, instead of once per slide. Under a slide transition such as `fade`, a per-slide mascot faded out with the old page while an identical one faded in with the new, a blink on every navigation; the deck-wide mascot stays put and only remounts (replaying its entrance) when the slide asks for a different picture, size or place. Print and export render the layer per printed slide; the overview and the presenter's next-slide preview keep a per-slide mascot. `Kodee` now takes `viewTransition` as a prop instead of reading the slide context, and the `kodee` value resolution lives in `kodee-props.ts`.
52
+ - Cross-slide Magic Move renders through a renderer that gathers the running animations once per step instead of asking the browser once per token. Shiki's renderer waits for every token with `getAnimations()`, which the browser answers by walking every running animation in the document, so a window of 138 tokens with three transitions each cost about 50 ms of one frame; the theme's `BatchedMagicMoveRenderer` (a subclass of the public `MagicMoveRenderer`) brings that to about 5 ms. On the kotlin-fundamentals deck the longest frame gap on a chain step went from 134 ms to 67 ms and no keypress stalls for 100 ms any more.
53
+ - Cross-slide Magic Move under a slide transition such as `fade`: the departing slide's copy of the code window is hidden for the length of the transition instead of cross-fading over the arriving window's morph, and the arriving window's box resizes from the first frame instead of after Shiki's default delay. The fade itself no longer touches the morphing window: Slidev's `fade` transitions the opacity of the whole slide root, so the window blinked out with the departing slide and faded back in halfway through its own animation. On a slide whose window is morphing, the root now stays opaque and only the content around the window cross-fades (the heading, annotations, Kodee, and any code window without a counterpart on the neighbouring slide); slides outside a chain fade as before. The README now recommends `fade` over `view-transition`, which makes Slidev 52 remount every preloaded slide whenever a click is followed by a slide change.
54
+ - Cross-slide Magic Move ships its precompiled steps as plain JSON (`steps-json`) instead of an lz-string payload. The lz string was already compressed, so gzip could not shrink it, and every slide of a chain carries the whole chain: on the kotlin-fundamentals deck the 173 payloads made up 1.1 MB of the 1.7 MB the built deck's JavaScript transferred, where the same steps as entity-escaped JSON gzip to a third of that. `JSON.parse` is also about ten times faster than the decode was (10 ms against 100 ms for every payload of that deck), so `MagicMoveBetween` parses each instance's own copy instead of sharing decoded steps, which also keeps `syncTokenKeys`'s in-place key rewrites from leaking between the windows of a chain. `lz-string` is no longer a dependency of the theme.
55
+ - The transformer highlights each step of a chain once per build or dev server instead of once per fence of the chain: every chain slide compiles all N steps, so a chain of N slides asked Shiki N² times, on every reload of a chain slide as well.
56
+ - `DrawnAnnotation` labels and the `message` of `InlineCompilerError`, `SmartCast`, and `Warning` are rendered by a small renderer of their own instead of markdown-it. The parser and its linkifier were the largest thing the theme added to the browser bundle (88 KB of JavaScript, 35 KB gzipped), loaded by every slide with an annotation, to render a label of a few words. The subset is what labels used: paragraphs, `>` quotes, fenced code, backticked inline code, `**strong**` or `__strong__`, `*emphasis*` or `_emphasis_` (an underscore inside a word stays text), and `\n` line breaks, with embedded HTML escaped. What markdown-it also accepted and no label in the decks used is now plain text: lists, headings, links and bare URLs, thematic breaks, backslash escapes, and HTML entities. The output keeps markdown-it's shape, so the label CSS is unchanged too. markdown-it remains a build-time dependency for the static handout.
57
+ - `DrawnAnnotation` imports only rough.js's generator, from `roughjs/bin/generator`, which is the copy Slidev's own `v-mark` already ships. `import rough from 'roughjs'` resolved to the library's separate bundled file, so built decks carried rough.js twice, together with the canvas and SVG renderers the component never uses.
58
+ - Code-window icons moved from the theme's `public/` directory to `assets/code-window/` and the stylesheet references them relatively. `url('/kotlin.svg')` only worked in a deck that had copied the icons into its own `public/`: Slidev copies a theme's `public/` to `dist/theme/`, never to the root, and Vite does not apply `--base` to a root-absolute URL it cannot find in the deck's public directory. Vite now bundles the icons like the Kodee artwork (see 0.0.9).
59
+ - `InlineCompilerError` skips its sync on slides that are not current. Every slide of the deck stays mounted, and each sync scanned the document's animations before checking whether anything was visible, so a whole-slideshow remount ran that scan once per diagnostic in the deck. Becoming current already schedules a full sync. `DrawnAnnotation` and `InlineCompilerError` also ask the document for its animations once per pass instead of once for the slide's subtree and once for the view transition's pseudo-elements, since the browser answers a subtree query by walking every animation in the document anyway.
60
+ - `SmartCast`, `Warning`, `TypeHint`, and `InlineValue` coalesce mutation and resize observer bursts into one sync per frame, as `InlineCompilerError` does: a Magic Move step flips classes on every token in several batches, and each sync walked the block, wrote classes, and forced a layout for the message.
61
+ - The theme's `index.html` opens connections to Google Fonts (`preconnect`) before the stylesheet Slidev links to is requested.
62
+ - `@slidev/parser` and `@slidev/types` accept any 52.x release (`^52.19.1`) instead of one exact version. A deck on a newer Slidev otherwise installed a second parser, whose slide slicing the preparser then had to agree with.
63
+ - The preparser no longer parses every slide's YAML to find slide boundaries; the boundaries come from the line scan, and Slidev parses the frontmatter itself right after.
64
+ - The published package includes `global-bottom.vue`, which draws the deck-wide Kodee, and the new `index.html`.
65
+ - The Shiki `v-mark` transformer and the development source transform skip fences and slide modules without a mark or an annotation before running their regexes; the static handout compiles its component regexes once.
66
+ - `scripts/test-annotation-editor.mjs` finds its fixture slide by its label instead of a hard-coded page number, which a slide added above it kept moving; `scripts/visual-review.mjs` reads the base revision's archive with a buffer large enough for the repository.
67
+ - `DrawnAnnotation` no longer re-searches the slide's text on every animation frame. While a Magic Move step or a slide transition moves the annotated text, the match found before the step is reused and only its client rects are read again; the search runs once per click, resize, or code-block mutation instead. The search itself no longer walks every text node's ancestors: an ignored subtree (the overlay, a token on its way out) is pruned once at its root. On a slide with three annotations over a cross-slide Magic Move this took the scripting of one navigation from about 150 ms to about 20 ms, most of which is Magic Move itself. Each measurement pass also reads the overlay's and the slide's client rects once, rather than once per coordinate conversion.
68
+ - `DrawnAnnotation` registered two window `keydown` listeners per mounted annotation, in built decks too, so every key press on a large deck ran several hundred handlers. One listener now serves the annotations of an open edit session, and only while it is open.
69
+ - `MagicMoveBetween` added its three `::view-transition-*` rules to the document once per code window; the windows of a chain now share one style element.
70
+ - `InlineCompilerError` walked a Magic Move block one TreeWalker per token span on every mutation; it walks the block once.
71
+ - `DrawnAnnotation` marks and `InlineCompilerError` diagnostics no longer redraw when Slidev rebuilds the slide they are on. Slidev's slideshow container changes shape on the first click after a view transition (and back on the next slide-to-slide navigation), which unmounts and remounts every loaded slide, so ink that was already on screen fell off the slide and animated in again on a click that changed nothing, and the slide being left rebuilt just before the browser snapshotted it. An instance that is unmounted while its ink is visible now hands that ink to the instance replacing it, through the same hand-off that already carries ink across a cross-slide Magic Move, and the replacement shows it complete in the same render pass, with no entrance animation. Ink that was not on screen before still animates in as usual.
72
+ - The development-only **Edit annotations** launcher no longer appears in PNG exports: `slidev export` renders through a development server, and the launcher only hid itself for print media. It now also stays out of the presenter view.
73
+ - A `DrawnAnnotation` tag that is byte-identical to one on another slide (the same `text` and `label`, as on the "before" and "after" slides of a cross-slide Magic Move chain) is editable again. The editor plugin used to receive slides only after Slidev had rendered their Markdown, so no line of the file could be found again and a tag was located by its identity alone, which such duplicates do not have. The plugin now runs ahead of Slidev's Markdown rendering, receives a slide as written, and locates a tag by the widest run of verbatim lines around it, which normally takes in the slide title; the identity fallback remains for a tag those lines cannot pin down.
74
+ - The launcher drops keyboard focus after a click: Slidev ignores its shortcuts while a button is focused, so leaving the editor with **Esc** used to leave the arrow keys dead until the author clicked somewhere else.
75
+ - A Kotlin `${...}` template reads as code, as IntelliJ shows it: the `${` and `}` take the escape colour that `$name` already had, and the expression between them the editor foreground. The grammar scopes the template as `meta.template.expression` with its own begin and end punctuation, and neither palette had a rule for those, so the whole template stayed string green while `$name` next to it turned blue. Both palettes now name the three scopes.
76
+
77
+ ### Changed
78
+
79
+ - Default code windows to 80 monospace columns instead of 68. Decks keep overriding `--code-window-columns` from their stylesheet.
80
+ - The annotation editor has no undo any more: the per-gesture history, its **Undo** button and **Cmd/Ctrl+Z** are gone. Re-drag what went wrong, or reset. A cancelled drag (Esc, or a pointer the browser interrupts) still snaps back to where it started.
81
+ - The editor toolbar's **Reset label** and **Reset label & connector** buttons are replaced by one **Reset annotation**, which removes the selected tag's whole `:geometry` binding; **Use automatic connector** is the one way back to the automatic route and removes the whole `connector` geometry, control point included. Resets, **Use automatic connector**, **Straighten connector** on a saved control point, and the connector toggle write immediately, since a draft cannot remove saved geometry.
82
+
5
83
  ## 0.12.1
6
84
 
7
85
  ### Fixed
package/README.md CHANGED
@@ -11,7 +11,6 @@ This theme provides a professional presentation template tailored for Kotlin dev
11
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
12
  - **Kotlin Syntax Highlighting**: Custom Shiki configuration optimized for Kotlin code
13
13
  - **Multiple Layouts**: Pre-built layouts including default, cover, and intro
14
- - **Kotlin Branding**: Official Kotlin logos for both light and dark modes
15
14
  - **JetBrains Fonts**: Prefers locally installed JetBrains Sans and JetBrains Mono, with Inter and Google-hosted JetBrains Mono fallbacks
16
15
  - **Drawn annotations**: Click-aware, hand-drawn marks, labels, and connectors for elements and code
17
16
  - **Inline compiler diagnostics**: IntelliJ-style squiggles and messages for static and Magic Move code blocks
@@ -45,14 +44,36 @@ export default defineConfig({
45
44
  })
46
45
  ```
47
46
 
48
- Open Slidev's annotation editor (or press **Alt+Shift+A**) and drag a visible
49
- label, its width handle, or a connector control. The editor persists normalized
50
- `:geometry` on exactly that `DrawnAnnotation` tag. **Cmd/Ctrl+Z** undoes a
51
- saved edit; reset controls restore label, connector, or all geometry. Source
52
- revisions are checked, so a conflict preserves the local draft until **Reload
53
- saved geometry** is chosen. The locator, toolbar, and write endpoint exist only
54
- while Vite serves the deck; builds and exports render saved geometry without
55
- editor code.
47
+ Click the theme's **Edit annotations** launcher (or press **Alt+Shift+A**) to start a frozen
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.
56
77
 
57
78
  ### Basic Setup
58
79
 
@@ -61,7 +82,7 @@ Add the theme to your slides' frontmatter:
61
82
  ```yaml
62
83
  ---
63
84
  theme: kotlin
64
- transition: view-transition
85
+ transition: fade
65
86
  ---
66
87
 
67
88
  # Your Presentation Title
@@ -101,6 +122,28 @@ or `r2dbc` modifier uses the Gradle, JDBC, or R2DBC identity respectively.
101
122
  Terminal, Maven, and PostgreSQL icons respectively. All other fences remain plain
102
123
  code surfaces.
103
124
 
125
+ A `console` fence is program output, not code: it renders as a flat panel in
126
+ the colours of IntelliJ's Run tool window, without the editor's outline and
127
+ shadow, marked with the green run icon. Put it under the Kotlin fence whose
128
+ output it shows:
129
+
130
+ ````md
131
+ ```kotlin
132
+ println([1, 2, 3].map { it + 1 })
133
+ ```
134
+
135
+ ```console
136
+ [2, 3, 4]
137
+ ```
138
+ ````
139
+
140
+ To show a value next to the line that produces it instead, as the debugger
141
+ does, wrap the fence in [`InlineValue`](components/README.md#inlinevalue).
142
+ The other IntelliJ decorations have a component each: [`SmartCast`](components/README.md#smartcast)
143
+ paints the green smart-cast background behind exact code, [`Warning`](components/README.md#warning)
144
+ the yellow warning background with an optional message, and [`TypeHint`](components/README.md#typehint)
145
+ the grey `this: HTML` inlay hint after a lambda's brace.
146
+
104
147
  The identity border colours are exposed as theme variables and can be
105
148
  customized from a deck stylesheet. Each variable has a light and dark-mode
106
149
  value:
@@ -116,19 +159,81 @@ value:
116
159
  --code-window-postgresql-color: #336791;
117
160
  --code-window-jdbc-color: #f59e0b;
118
161
  --code-window-r2dbc-color: #06b6d4;
162
+
163
+ /* The `console` output panel. */
164
+ --code-window-output-background: #f7f8fa;
165
+ --code-window-output-color: #2b2d30;
119
166
  }
120
167
 
121
168
  html.dark {
122
169
  --code-window-kotlin-color: #a571e6;
170
+ --code-window-output-background: #1e1f22;
171
+ --code-window-output-color: #bcbec4;
123
172
  /* Override any of the other dark-mode values as needed. */
124
173
  }
125
174
  ```
126
175
 
176
+ ### Folded imports
177
+
178
+ A snippet on a slide reads best without its imports, but a snippet that
179
+ carries them is complete: it compiles as written, a snippet checker such as
180
+ Knit can verify it, and the deck's handout (see below) shows a reader which
181
+ package every symbol comes from. So write the imports, and the theme folds
182
+ them away:
183
+
184
+ ````md
185
+ ```kotlin
186
+ import kotlinx.coroutines.flow.Flow
187
+ import kotlinx.coroutines.flow.flowOf
188
+
189
+ fun numbers(): Flow<Int> = flowOf(1, 2, 3)
190
+ ```
191
+ ````
192
+
193
+ On the slide, the code window shows only `fun numbers()…`; the handout and
194
+ `llms-full.txt` print the fence in full.
195
+
196
+ When an example needs more than imports — a table definition, a helper the
197
+ example calls — put a `// Example` comment where the example starts.
198
+ Everything above it leaves the slide, and so does the marker line itself:
199
+
200
+ ````md
201
+ ```kotlin
202
+ import kotlinx.coroutines.flow.Flow
203
+ import kotlinx.coroutines.flow.flowOf
204
+ import kotlinx.coroutines.flow.map
205
+
206
+ fun numbers(): Flow<Int> = flowOf(1, 2, 3)
207
+
208
+ // Example: reuse the flow above without showing it
209
+ fun doubled(): Flow<Int> = numbers().map { it * 2 }
210
+ ```
211
+ ````
212
+
213
+ The window shows `fun doubled()…`. The comment is ordinary Kotlin, so the
214
+ fence still compiles as written (see the snippet checker below), and the
215
+ handout keeps it. A description after the colon is optional.
216
+
217
+ Details:
218
+
219
+ - Kotlin, Java, and Scala fences (`kotlin`, `kt`, `kts`, `java`, `scala`)
220
+ fold. Without a marker, only a block of `import` lines at the top of the
221
+ fence counts, and a `package` line or `@file:` annotation before it stays
222
+ visible. With a marker, everything above it folds. Only the first
223
+ `// Example` line in a fence counts, and only when there is code above it.
224
+ - Line numbers, `{1|2-3}` highlight ranges, and annotation targets all
225
+ count from the first line of real code, so folding never shifts them.
226
+ - Cross-slide Magic Move chains and classic ````md magic-move```` blocks fold
227
+ too, step by step.
228
+ - Add `show-imports` to a fence (`` ```kotlin show-imports ``) when the slide
229
+ is about the imports themselves, or set `themeConfig: { foldImports: false }`
230
+ in the headmatter to keep every import visible.
231
+
127
232
  ### Code sizing
128
233
 
129
234
  Every code window uses one font size calculated from the slide canvas width. By
130
- default, a full-width window fits 68 monospace characters. Override the column
131
- count once in the deck's stylesheet:
235
+ default, a full-width window fits 80 monospace characters. Override the column
236
+ count once in the deck's stylesheet, or narrower on a single slide class:
132
237
 
133
238
  ```css
134
239
  :root {
@@ -139,7 +244,13 @@ count once in the deck's stylesheet:
139
244
  The calculation accounts for the standard layout and code padding. A custom
140
245
  layout with different horizontal spacing can override
141
246
  `--code-window-inline-space`. To bypass column-based sizing entirely, set an
142
- explicit `--code-window-font-size`, for example `1.25rem`.
247
+ explicit `--code-window-font-size`, for example `1.25rem`. The font's advance
248
+ width and the line height the calculation assumes are
249
+ `--code-window-character-width` (`0.61`, JetBrains Mono's) and
250
+ `--code-window-line-height` (`1.42`); a deck on another monospace font sets the
251
+ first to that font's ratio. The editor surface itself reads `--editor-bg`,
252
+ `--editor-fg`, `--editor-border`, and `--editor-shadow`, and one window's
253
+ border colour can be overridden with `--code-window-border`.
143
254
 
144
255
  ### Magic Move between slides
145
256
 
@@ -176,9 +287,16 @@ Details:
176
287
  parallel snippets (say Kotlin next to SQL) each animate independently.
177
288
  - Everything outside the code fences is a normal slide — headings, prose, and
178
289
  layout can change freely and follow the deck's regular slide transition.
179
- - With `transition: view-transition`, the code windows of both slides are
180
- paired through the View Transitions API, so the window glides to its new
181
- position while the tokens morph, instead of cross-fading with the page.
290
+ - With a slide transition such as `fade`, the departing slide's copy of the
291
+ window is hidden for the length of the transition, so only the arriving
292
+ window is visible while its tokens morph and its box resizes to the new
293
+ code. Prefer `fade` over `view-transition`: with `transition:
294
+ view-transition`, Slidev 52 switches its slideshow container between a
295
+ `div` and a `TransitionGroup` whenever a click is followed by a slide
296
+ change, which remounts every preloaded slide of the deck and stalls the
297
+ frame for 150 ms or more on each such navigation. If you do use
298
+ `view-transition`, the code windows of both slides are paired through the
299
+ View Transitions API instead.
182
300
  - Navigating backward plays the animation in reverse. Deep links, the
183
301
  overview, and PDF/PNG exports render each slide's own step statically.
184
302
  - Fence metadata still works: the code-window identity icons, `[Title]`
@@ -221,6 +339,18 @@ kodee:
221
339
  ---
222
340
  ```
223
341
 
342
+ On `cover` and `intro` slides the text sits in a column beside the featured
343
+ mascot and wraps inside it instead of running under Kodee. The column is
344
+ `--cover-text-width`, 48% of the content box by default, which clears the
345
+ mascot's arm. Widen it in the deck's stylesheet when the titles are short, or
346
+ give a slide that hides the mascot the full width:
347
+
348
+ ```css
349
+ .slidev-layout.cover.no-mascot {
350
+ --cover-text-width: 100%;
351
+ }
352
+ ```
353
+
224
354
  #### Kodee Configuration Options
225
355
 
226
356
  - **variant**: Choose from available Kodee images
@@ -246,6 +376,8 @@ kodee:
246
376
  - `featured` - Prominently displayed (cover/intro default)
247
377
  - `custom` - Use with `x` and `y` coordinates
248
378
 
379
+ - **scale**: Multiply the size, `1.2` for a fifth larger. Defaults to `1`.
380
+
249
381
  #### Custom Positioning
250
382
 
251
383
  For precise control, use custom positioning:
@@ -263,7 +395,7 @@ kodee:
263
395
 
264
396
  ### Using DrawnAnnotation
265
397
 
266
- `<DrawnAnnotation>` is auto-registered for every deck using the theme. It can mark an element with a selector or exact text inside a Shiki or Magic Move code block, then connect that mark to a label or another element. Labels accept safe Markdown: backticked inline code and `>` quotes use the local-first JetBrains Mono stack.
398
+ `<DrawnAnnotation>` is auto-registered for every deck using the theme. It can mark an element with a selector or exact text inside a Shiki or Magic Move code block, then connect that mark to a label or another element. Labels accept a small, safe Markdown subset (backticked inline code, `>` quotes, `**strong**` or `__strong__`, `*emphasis*` or `_emphasis_`, and line breaks; HTML is escaped): inline code and quotes use the local-first JetBrains Mono stack.
267
399
 
268
400
  ````md
269
401
  <DrawnAnnotation type="circle" text="fun main" label="program entry point" :on="1">
@@ -313,6 +445,167 @@ theme: kotlin
313
445
 
314
446
  The theme's example deck includes this guard.
315
447
 
448
+ ## Static handout, `llms.txt`, and hash-mode links
449
+
450
+ A Slidev build is a single-page app: without JavaScript its `index.html`
451
+ contains nothing but the title. Search engines render JavaScript for the root
452
+ URL at best, and AI crawlers (GPTBot, ClaudeBot, CCBot, …) render none, so the
453
+ code on your slides never reaches them. Every `slidev build` on this theme
454
+ therefore also writes, next to the app:
455
+
456
+ - `handout/index.html` and `handout/<section>.html` — one page per source
457
+ file (`src:` lesson), with every slide's title, prose, and highlighted code,
458
+ each linking back to the live slide. A single-file deck gets one page.
459
+ - `llms.txt` and `llms-full.txt` — the [llmstxt.org](https://llmstxt.org)
460
+ convention that coding agents and AI crawlers look for: an index, and the
461
+ full Markdown of the deck with complete code snippets (folded imports
462
+ expanded).
463
+ - `sitemap.xml` — only when the deck's public URL is known.
464
+ - `robots.txt` — only when that URL is the root of its host and `public/`
465
+ ships none. Crawlers read `robots.txt` at the host root alone, so a deck
466
+ served from a sub-path (a GitHub project page, a folder of your blog) gets
467
+ no file; add `Sitemap: <siteUrl>sitemap.xml` to the host's own
468
+ `robots.txt` instead, as the build reminds you.
469
+
470
+ The handout and `llms-full.txt` form one outline: the deck, then each
471
+ section, then each slide, with a slide's own headings placed below its
472
+ title. The handout pages carry a description, canonical URL, Open Graph
473
+ tags, and the deck's `lang` from the headmatter (`en` when unset).
474
+
475
+ The built `index.html` (and the `404.html` copied from it) is made
476
+ self-describing too: `rel="alternate"` links to the handout and
477
+ `llms-full.txt`, a plain-text `<meta name="description">` from the headmatter
478
+ `info` (replacing the one Slidev renders from the raw Markdown), and a short
479
+ paragraph linking to the same files as visible text, hidden as soon as the
480
+ app has mounted. That paragraph is ordinary markup rather than `<noscript>`,
481
+ which many text extractors drop, so a crawler that renders no JavaScript
482
+ still finds the content from the root URL. With `siteUrl` set the page also
483
+ carries the canonical URL and a `rel="sitemap"` link.
484
+
485
+ Theme markup is translated, not dropped: a `DrawnAnnotation` with a `label`
486
+ becomes a quoted note about the code it points at, an `InlineCompilerError`
487
+ becomes its diagnostic, Magic Move blocks become their steps, and
488
+ `<<< @/file#region` snippet imports are inlined.
489
+
490
+ Give the deck its public URL so links, the sitemap, and `llms.txt` are
491
+ absolute, and opt out of notes or the whole feature if you need to:
492
+
493
+ ```yaml
494
+ themeConfig:
495
+ siteUrl: https://example.github.io/my-deck/
496
+ handout:
497
+ notes: false # leave presenter notes out (default: included)
498
+ # handout: false # disable the static content entirely
499
+ ```
500
+
501
+ ## Analytics
502
+
503
+ Decks can count pageviews with [GoatCounter](https://www.goatcounter.com),
504
+ which sets no cookies and stores no personal data, so it needs no consent
505
+ banner. Give the headmatter the endpoint of your GoatCounter site, or just
506
+ its code:
507
+
508
+ ```yaml
509
+ themeConfig:
510
+ analytics:
511
+ goatcounter: https://mysite.goatcounter.com/count # or: mysite
512
+ ```
513
+
514
+ Every `slidev build` then adds `count.js` to the deck, and the theme sends
515
+ one pageview per slide, recorded as `<base>/<slide number>` with the slide's
516
+ title, so the dashboard's path list doubles as a per-slide drop-off curve.
517
+ Clicks within a slide, the presenter and overview views, notes, print, and
518
+ export are never counted. Handout pages carry the tag too and count under
519
+ their own URL. `slidev dev`, exports, and screenshots send nothing:
520
+ `count.js` ignores localhost and headless browsers by itself.
521
+
522
+ Every deck on one host can share one GoatCounter site, since paths carry the
523
+ deck's base. Add `#toggle-goatcounter` to a deck's URL to stop counting your
524
+ own visits in that browser.
525
+
526
+ ## Compiling the snippets
527
+
528
+ A Kotlin fence on a slide is a fragment: it carries its imports (folded away,
529
+ see above) but no package, and it may mix declarations with bare statements.
530
+ `slidev-kotlin-snippets` turns Kotlin and Java fences into source files, in the
531
+ spirit of [KotlinX Knit](https://github.com/Kotlin/kotlinx-knit), so a Gradle
532
+ module can compile the deck's code against the real libraries and a stale slide
533
+ fails the build instead of the talk.
534
+
535
+ Turn it on in the headmatter and run the CLI from the deck:
536
+
537
+ ```yaml
538
+ themeConfig:
539
+ snippets:
540
+ dir: src/main/kotlin/presentation/snippets # default: src/main/kotlin/snippets
541
+ javaDir: src/main/java/presentation/snippets # default: src/main/java/snippets
542
+ package: presentation.snippets # default: snippets
543
+ imports: # added to every file
544
+ - presentation.support.*
545
+ ```
546
+
547
+ ```bash
548
+ npx slidev-kotlin-snippets # writes the files, removes stale ones
549
+ npx slidev-kotlin-snippets --check # exits 1 when they are out of date (CI)
550
+ npx slidev-kotlin-snippets talk.md # another entry than slides.md
551
+ ```
552
+
553
+ Each Kotlin fence becomes `<dir>/<section>/<slide>-<title>.kt`; each Java
554
+ fence becomes `<javaDir>/<section>/<slide>-<title>.java`. Files have a package,
555
+ `<package>.<section>.slide<n>`, so every step of a Magic Move chain can declare
556
+ its own `object Talks`. A Java and Kotlin fence on the same slide deliberately
557
+ share that package, allowing Java ↔ Kotlin interop examples to compile. Inside
558
+ the file:
559
+
560
+ - the fence's imports come first, after the `imports` from the config. Keep
561
+ the tables, types, and values the slides use without defining them in a
562
+ hand-written package and star-import it; a fence that defines its own
563
+ `Talks` shadows the shared one, because a declaration in the file's own
564
+ package wins over a star import.
565
+ - Kotlin top-level declarations (`fun`, `val`, `object`, `class`, ... with
566
+ their annotations, modifiers, and `context(...)` lines) stay top-level, and
567
+ bare statements such as `Talks.selectAll()` are wrapped in `suspend fun main()`.
568
+ A Kotlin `package` line is dropped; `@file:` annotations move above the package.
569
+ - Java fences are emitted as Java compilation units with the generated package
570
+ and imports; they must therefore contain declarations rather than bare
571
+ statements. Their own `package` line is replaced. A fence declaring a public
572
+ top-level Java type is named after that type (as Java requires); otherwise it
573
+ uses the slide-based filename. When a later slide of the same section declares
574
+ a public type an earlier slide already declared, its file goes in a directory
575
+ named after the slide, so both compile.
576
+ - a slide with several fences in the same language tells them apart by their
577
+ first meta word (`jdbc`, `r2dbc`) or a letter.
578
+
579
+ Not every fence is meant to compile. Add `no-compile` to a fence that sketches
580
+ library internals or an intermediate step (`` ```kotlin no-compile ``); the flag
581
+ is stripped before Slidev sees it. Fences inside an `InlineCompilerError` are
582
+ skipped automatically, since the slide shows the diagnostic on purpose, and so
583
+ are `gradle` build-script fences.
584
+
585
+ Wire it into Gradle so `./gradlew check` also fails on stale sources:
586
+
587
+ ```kotlin
588
+ val snippetsCheck by tasks.registering(Exec::class) {
589
+ commandLine("npx", "slidev-kotlin-snippets", "--check")
590
+ }
591
+ tasks.check { dependsOn(snippetsCheck) }
592
+ ```
593
+
594
+ ### Deep links on GitHub Pages
595
+
596
+ GitHub Pages has no single-page-app rewrite, so a history-mode deep link such
597
+ as `/my-deck/12` is answered by the copied `404.html` with a 404 status. Build
598
+ with Slidev's hash router instead:
599
+
600
+ ```bash
601
+ slidev build --base /my-deck/ --router-mode hash
602
+ ```
603
+
604
+ Links then look like `/my-deck/#/12` and always resolve. The theme adds a small
605
+ script to hash-mode builds that forwards old `/my-deck/12` links (and
606
+ `presenter/…`, `overview`, `notes`, `print`, `export`) to their hash
607
+ equivalent, so links shared before the switch keep working.
608
+
316
609
  ## Development
317
610
 
318
611
  ### Available Scripts
@@ -370,20 +663,44 @@ This will open the example presentation in your browser with hot-reload enabled.
370
663
  .
371
664
  ├── components/ # Vue components
372
665
  │ ├── DrawnAnnotation.vue # Hand-drawn marks, labels, and connectors
666
+ │ ├── DrawnAnnotationEditor*.vue # Development-only editor launcher and toolbar
667
+ │ ├── DrawnAnnotationProblems.vue # Development-only problem chips
668
+ │ ├── drawn-annotation/ # Editor store, geometry, and browser writer
373
669
  │ ├── InlineCompilerError.vue # Inline code diagnostics
670
+ │ ├── SmartCast.vue # Smart-cast background behind exact code
671
+ │ ├── Warning.vue # Warning background with an optional message
672
+ │ ├── TypeHint.vue # Inlay hint after a lambda's brace
673
+ │ ├── InlineValue.vue # Debugger inline value after a line
674
+ │ ├── code-highlight.ts # The mechanism the four above share
675
+ │ ├── code-text-match.ts # Text search shared with DrawnAnnotation
676
+ │ ├── MagicMoveBetween.vue # Cross-slide Magic Move code window
374
677
  │ ├── Kodee.vue # Main Kodee mascot component with animations
375
678
  │ └── KodeeWrapper.vue # Wrapper component for Kodee integration
376
- ├── slide-bottom.vue # Per-slide Kodee layer shared by every layout
679
+ ├── global-top.vue # Development-only editor toolbar and problem chips
680
+ ├── global-bottom.vue # Deck-wide Kodee layer that follows the current slide
681
+ ├── slide-bottom.vue # Per-slide Kodee for the overview and presenter preview
682
+ ├── plugins/ # Development-only source writer (`slidev-theme-kotlin/annotation-editor`)
683
+ ├── bin/ # `slidev-kotlin-snippets` CLI
377
684
  ├── layouts/ # Slidev layout templates
378
685
  │ ├── cover.vue # Cover slide layout
379
686
  │ ├── default.vue # Default slide layout
380
687
  │ └── intro.vue # Introduction slide layout
381
688
  ├── assets/ # Bundled theme assets
382
- │ └── kodee-*.svg # Kodee mascot variants (11 variants)
383
- ├── public/ # Static assets
384
- │ └── kotlin-logo*.svg # Kotlin logos (light/dark modes)
689
+ │ ├── kodee-*.svg # Kodee mascot variants (11 variants)
690
+ │ └── code-window/ # Code-window identity icons, referenced from layout.css
691
+ ├── index.html # Head tags merged into every deck (font preconnect)
385
692
  ├── setup/ # Slidev setup files
386
- │ └── shiki.ts # Syntax highlighting configuration for Kotlin
693
+ │ ├── main.ts # App setup: per-slide analytics pageviews
694
+ │ ├── preparser.ts # The bare `magic-move` slide separator
695
+ │ ├── shiki.ts # Syntax highlighting configuration for Kotlin
696
+ │ ├── transformers.ts # Cross-slide Magic Move and import folding passes
697
+ │ ├── magic-move-between.ts # Precompiled Magic Move steps
698
+ │ ├── analytics.ts # GoatCounter tag and per-slide pageviews
699
+ │ ├── code-imports.ts # Folded imports: fence scanning and source edits
700
+ │ ├── kotlin-snippets.ts # Compilable snippet files per fence
701
+ │ ├── static-content.ts # Handout, llms.txt, sitemap generation
702
+ │ ├── hash-route-redirect.ts # Old history-mode links → hash routes
703
+ │ └── vite-plugins.ts # Registers the three build plugins above
387
704
  ├── styles/ # Theme styles
388
705
  │ ├── index.ts # Style entry point
389
706
  │ └── layout.css # Layout styles
@@ -407,7 +724,7 @@ capture every settled click state from Slidev's normal presenter/player route
407
724
  It renders the current working tree and a Git revision with the same browser,
408
725
  compares every PNG with `pixelmatch`, and writes a three-column
409
726
  base/current/diff page. When the local
410
- `~/Developer/kotlin-fundamentals` consumer deck is available, its baseline is
727
+ `../kotlin-fundamentals` consumer deck is available, its baseline is
411
728
  rendered from its clean `HEAD` and its current capture includes its working-tree
412
729
  changes, with each capture linked to the corresponding theme revision.
413
730
  Screenshots are not committed: the chosen Git revision is the baseline, so
@@ -453,8 +770,11 @@ The theme is configured with the following defaults in `package.json`:
453
770
  "colorSchema": "both",
454
771
  "defaults": {
455
772
  "fonts": {
456
- "sans": "Inter",
457
- "mono": "JetBrains Mono"
773
+ "sans": "JetBrains Sans,Inter",
774
+ "mono": "JetBrains Mono Local,JetBrains Mono",
775
+ "local": "JetBrains Sans,JetBrains Mono Local",
776
+ "weights": "400,700,800",
777
+ "italic": true
458
778
  }
459
779
  }
460
780
  }
@@ -483,7 +803,10 @@ layout: cover
483
803
 
484
804
  ### Runtime Dependencies
485
805
  - `@shikijs/magic-move` (4.4.3) - Code-token morphing for Magic Move
486
- - `@slidev/types` (52.19.1) - Slidev type definitions
806
+ - `@slidev/parser` (^52.19.1) - Deck parsing for the snippets CLI and lint script
807
+ - `@slidev/types` (^52.19.1) - Slidev type definitions
808
+ - `jiti` (2.7.0) - Runs the theme's TypeScript setup modules from the CLI
809
+ - `markdown-it` (14.3.0) - Renders the static handout
487
810
  - `roughjs` (4.6.6) - Hand-drawn SVG paths for `DrawnAnnotation`
488
811
 
489
812
  ### Development Dependencies
@@ -509,7 +832,7 @@ This project is licensed under the Apache License 2.0. See the [LICENSE](LICENSE
509
832
 
510
833
  - [Slidev Documentation](https://sli.dev)
511
834
  - [Kotlin Official Website](https://kotlinlang.org)
512
- - [Package on npm](https://www.npmjs.com/package/@nomisrev/slidev-theme-kotlin)
835
+ - [Package on npm](https://www.npmjs.com/package/slidev-theme-kotlin)
513
836
 
514
837
  ---
515
838