feat(slides): Slidev deck for the Kotlin Nexus workshop - #11
Merged
Conversation
Slidev 52. slides.md holds headmatter plus src: imports only; each segment is its own file so one can be rewritten or cut without touching its neighbours. The theme is vendored at slides/theme rather than installed as a dependency. `pnpm add github:temporalio/slidev-theme-temporal` installs fine and then fails the build: pnpm names the store directory for a git dep with a #<commit> suffix, and Vite's CSS url() resolver treats # as a fragment separator, so the theme's five background images resolve to a truncated path and error with ENOENT. Vendoring is Option 2 in the theme's own README and what the Replay workshop deck does. Also pins prettier-plugin-slidev. Without it a format-on-save pass rewrites every per-slide frontmatter block: in Markdown a line of text followed by --- is a setext H2, which is exactly what Slidev frontmatter looks like, so `layout: default` between two --- lines becomes the heading "## layout: default" and the slide loses its layout entirely. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A project-local layout rather than an edit to the vendored theme, so pulling a newer theme cannot clobber it. Code across the top, definition across the full width below. The definition takes only the height it needs and the code claims the slack, so a one-line definition hands its space to the code instead of reserving an empty third of the slide. Both panes then measure themselves after render and step their font size down until the content fits, watching for resizes and content changes, so no slide scrolls or clips. Three CSS traps are documented in comments, each found by measuring the live DOM rather than reasoning: - `title` is reserved by Slidev for slide metadata and never reaches a layout as a prop, so the heading prop is `heading`. - minmax(0, auto) collapses the prose track: with a zero floor its min-content contribution is zero and the greedy 1fr code row eats the space. - A percentage max-height on a grid item resolves against its own track, so capping an auto-sized track at 40% yields 40% of its own content. fit-content() resolves against the grid container, which is what we want. scripts/probe-fit.mjs drives the dev server with Playwright and reports the settled font size and overflow for every code slide. It caught two clipping bugs that eyeballing missed, and it walks the deck rather than hardcoding slide numbers so it survives slides being added or removed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three opener slides, a section divider, then one definition-plus-code slide per Nexus concept: Service, Operation, Endpoint, Caller, Handler, sync vs async. Every concept is taught with real Kotlin from the solution tree before anyone opens the editor, which lets segments 2 through 4 shrink to recall rather than first exposure. Registry is deliberately absent from the concept block. The lab names it once in challenge 2's notes and no challenge ever touches it. The architecture diagram on the before-and-after slide comes from temporalio/temporal-learning -> docs/tutorials/nexus/ui/architecture-overview.svg, rendered in the Overview of the Java sync tutorial. It labels exactly the pieces this workshop builds. Bound as :src rather than src: Vite statically analyses a literal src in the compiled Vue template and tries to resolve it as a module, which fails for a public/ path inside Slidev's virtual modules. Presenter notes on each slide carry the source file:line for the snippet and, where a definition is still unwritten, what it needs to establish. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a Slidev-based slide deck for the Kotlin Nexus workshop and vendors an expanded slidev-theme-temporal theme (layouts/components/styles + example decks) to support the workshop’s design and pacing goals.
Changes:
- Introduces a full Slidev deck under
kotlin/slides/, segmented into workshop sections and backed by a Playwright-based “fit probe” script for code-slide overflow checks. - Vendors and extends the Temporal Slidev theme under
kotlin/slides/theme/(palette + Shiki + Mermaid + 29 layouts + reusable components + example decks + scaffolding script). - Adds a project-local
code-stacklayout to support “code + definition” slides with auto-fitting panes.
Reviewed changes
Copilot reviewed 70 out of 77 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| kotlin/slides/theme/styles/layout.css | Global palette, typography, layout primitives, and background helpers for the vendored theme |
| kotlin/slides/theme/styles/index.ts | Theme styles entrypoint wiring layout + code CSS |
| kotlin/slides/theme/styles/code.css | Code block typography and panel styling |
| kotlin/slides/theme/spec.md | Theme parity/audit spec vs 2026 PPT template and build plan/reference |
| kotlin/slides/theme/setup/temporal.json | Shiki token theme JSON for “temporal” |
| kotlin/slides/theme/setup/shiki.ts | Registers Shiki theme for Slidev |
| kotlin/slides/theme/setup/mermaid.ts | Mermaid themeVariables aligned with Temporal palette |
| kotlin/slides/theme/scripts/new-deck.sh | Script to scaffold a new deck wired to the vendored theme |
| kotlin/slides/theme/README.md | Theme documentation (usage, layouts, examples, brand rules) |
| kotlin/slides/theme/package.json | Theme package metadata + Slidev defaults |
| kotlin/slides/theme/LICENSE | Theme license |
| kotlin/slides/theme/layouts/two-cols.vue | Two-column layout with footer + grid background |
| kotlin/slides/theme/layouts/two-cols-header.vue | Two-column layout with shared title + optional eyebrows |
| kotlin/slides/theme/layouts/toc.vue | Agenda/TOC layout using configurable TOC component |
| kotlin/slides/theme/layouts/timeline.vue | Timeline layout using TimelineRail component |
| kotlin/slides/theme/layouts/success-story.vue | Customer success story layout with logo slot + challenges/solution columns |
| kotlin/slides/theme/layouts/subsection.vue | Sub-section divider layout with eyebrow label |
| kotlin/slides/theme/layouts/section.vue | Section divider layout |
| kotlin/slides/theme/layouts/release-stages.vue | 3-stage release explainer layout |
| kotlin/slides/theme/layouts/quote.vue | Quote card layout with avatar + attribution |
| kotlin/slides/theme/layouts/qa.vue | Minimal Q&A divider layout |
| kotlin/slides/theme/layouts/profile.vue | Profile card layout with avatar + attribution block |
| kotlin/slides/theme/layouts/model-diagram.vue | Model diagram layout with asset-pending placeholder slot |
| kotlin/slides/theme/layouts/image-right.vue | Text-left, image-right layout |
| kotlin/slides/theme/layouts/image-left.vue | Image-left, text-right layout |
| kotlin/slides/theme/layouts/feature-grid.vue | N-column feature grid layout driven by items prop |
| kotlin/slides/theme/layouts/feature-card.vue | Feature card layout with release-stage chip + 3 body slots |
| kotlin/slides/theme/layouts/eyebrow-hero.vue | Eyebrow + large heading hero layout |
| kotlin/slides/theme/layouts/exercise.vue | Workshop exercise timer layout |
| kotlin/slides/theme/layouts/end.vue | Closing slide layout with logo + brand rule enforcement |
| kotlin/slides/theme/layouts/default.vue | Default content layout with footer pinned via flex |
| kotlin/slides/theme/layouts/cta-icons.vue | CTA icon grid layout (asset-pending icons) |
| kotlin/slides/theme/layouts/cta-banner.vue | Single CTA banner layout |
| kotlin/slides/theme/layouts/cover.vue | Cover layout using BackgroundLayer variants + logo brand rule |
| kotlin/slides/theme/layouts/comparison.vue | Two-card comparison layout with checkmark feature lists |
| kotlin/slides/theme/layouts/code-explain.vue | Code + prose side-by-side layout |
| kotlin/slides/theme/layouts/checklist.vue | Checklist grid layout with checkmarks |
| kotlin/slides/theme/layouts/chart.vue | Chart slot + caption layout |
| kotlin/slides/theme/layouts/big-stat.vue | Big stat hero layout with optional ribbon slot |
| kotlin/slides/theme/layouts/architecture.vue | Architecture diagram layout with before/after variant + placeholder slot |
| kotlin/slides/theme/justfile | Dev/build/export recipes for running theme example decks |
| kotlin/slides/theme/example/workshop.md | Example workshop deck demonstrating TOC + exercise layout |
| kotlin/slides/theme/example/slides.md | Full reference deck exercising every layout |
| kotlin/slides/theme/example/README.md | Documentation for running/maintaining example decks |
| kotlin/slides/theme/example/feature-launch.md | Example mini-deck for feature launches |
| kotlin/slides/theme/example/customer-story.md | Example mini-deck for customer stories |
| kotlin/slides/theme/components/WorkshopToc.vue | Configurable TOC driven by themeConfig.toc |
| kotlin/slides/theme/components/TimelineRail.vue | Timeline rail primitive used by timeline layout |
| kotlin/slides/theme/components/TemporalLogo.vue | Inline SVG Temporal mark with brand rule reminder |
| kotlin/slides/theme/components/TemporalFooter.vue | Footer component (deck title + page indicator) |
| kotlin/slides/theme/components/ReleaseStageChip.vue | Pill chip for release stages |
| kotlin/slides/theme/components/QuoteAttribution.vue | Name + role attribution block |
| kotlin/slides/theme/components/ProfileAvatar.vue | Avatar primitive with placeholder fallback |
| kotlin/slides/theme/components/FeatureBlock.vue | Title/body block primitive |
| kotlin/slides/theme/components/Checkmark.vue | Inline SVG checkmark primitive |
| kotlin/slides/theme/components/BackgroundLayer.vue | Background variant → CSS class mapping wrapper |
| kotlin/slides/theme/CHANGELOG.md | Theme changelog describing parity work and breaking changes |
| kotlin/slides/slides.md | Main Slidev deck entry referencing segment files |
| kotlin/slides/segments/06-close.md | Placeholder workshop segment 6 |
| kotlin/slides/segments/05-durability.md | Placeholder workshop segment 5 |
| kotlin/slides/segments/04-caller.md | Placeholder workshop segment 4 |
| kotlin/slides/segments/03-handlers.md | Placeholder workshop segment 3 |
| kotlin/slides/segments/02-contract.md | Placeholder workshop segment 2 |
| kotlin/slides/segments/01-coupling.md | Placeholder workshop segment 1 |
| kotlin/slides/segments/00-open.md | Segment 0 draft (concept slides + diagram slide) |
| kotlin/slides/scripts/probe-fit.mjs | Playwright-driven overflow/font-size probe for code-stack slides |
| kotlin/slides/public/nexus-architecture.svg | Animated architecture diagram asset used in slides |
| kotlin/slides/package.json | Slides project devDependencies + Slidev scripts |
| kotlin/slides/layouts/code-stack.vue | Project-local auto-fitting code+definition layout |
| kotlin/slides/.prettierrc | Prettier config using prettier-plugin-slidev for deck/segments |
| kotlin/slides/.gitignore | Slidev/build output ignore rules |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+119
to
+122
| # Copy the example deck, rewriting its in-repo `theme: ../` reference to | ||
| # the relative path from the new deck back to the theme repo. | ||
| sed "s|^theme: \.\./\$|theme: $rel_theme|" "$src_file" > "$target_abs/slides.md" | ||
| fi |
Comment on lines
+25
to
+30
| <template> | ||
| <div class="temporal-footer"> | ||
| <span>{{ $slidev.themeConfigs?.footer ?? '' }}</span> | ||
| <span class="page">{{ pageLabel }}</span> | ||
| </div> | ||
| </template> |
Comment on lines
+1
to
+38
| <!-- | ||
| ABOUTME: Inline SVG check mark used by checklist and POC layouts. | ||
| ABOUTME: Inherits color via currentColor so any layout can tint it. | ||
|
|
||
| Usage: | ||
| <Checkmark /> | ||
| <Checkmark label="Completed" /> <!-- announces to screen readers --> | ||
| --> | ||
| <script setup lang="ts"> | ||
| defineProps<{ label?: string }>() | ||
| </script> | ||
|
|
||
| <template> | ||
| <svg | ||
| class="checkmark" | ||
| viewBox="0 0 24 24" | ||
| fill="none" | ||
| stroke="currentColor" | ||
| stroke-width="2.5" | ||
| stroke-linecap="round" | ||
| stroke-linejoin="round" | ||
| :aria-hidden="label ? undefined : 'true'" | ||
| :aria-label="label" | ||
| :role="label ? 'img' : undefined" | ||
| > | ||
| <polyline points="4 12 10 18 20 6" /> | ||
| </svg> | ||
| </template> | ||
|
|
||
| <style scoped> | ||
| .checkmark { | ||
| display: inline-block; | ||
| width: 1.1em; | ||
| height: 1.1em; | ||
| color: var(--temporal-green); | ||
| vertical-align: -0.15em; | ||
| } | ||
| </style> |
Comment on lines
+156
to
+173
| ```kotlin {1-8|10|all} | ||
| //The Caller Workflow | ||
| public val NexusCallerWorkflowImpl implements NexusCallerWorkflow { | ||
|
|
||
| private val complianceService: ComplianceNexusService = | ||
| //Nexus stub: The delivery app. Use it to place orders | ||
| Workflow.newNexusServiceStub( | ||
| //The menu tells you what you can order | ||
| ComplianceNexusService::class.java, | ||
| NexusServiceOptions.newBuilder() | ||
| .setOperationOptions(/* scheduleToCloseTimeout */) | ||
| .build(), | ||
| ) | ||
|
|
||
| // ... | ||
| //One of the menu items is checkCompliance() | ||
| val compliance = complianceService.checkCompliance(compReq) | ||
| ``` |
A cta-banner slide carrying t.mp/nexus-kotlin, the one URL attendees have to type all day. Sits last in segment 0 so it can stay up while the sandbox warms. Distinct from the per-challenge handoffs, which use the theme's exercise layout and its countdown timer at the end of each later segment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drops the subheadline and rebuilds the slide as two full-height halves: the portrait clip edge to edge on the left, eyebrow/headline/link on the right. cta-banner could not do this -- it centres a single column -- so the slide is inline markup using the theme's own colour variables rather than a new layout file for one slide. object-cover crops the letterbox bars baked into the source. The .mov is remuxed to .mp4 with -c copy: the codecs were already H.264/AAC, so it is lossless, and Chromium's .mov support is inconsistent, which matters because PDF export runs through Chromium. +faststart so it starts without buffering the whole file. Bound as :src for the same reason as the SVG. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rebuilt from slide 2 of the Agentic AI Summit 2026 deck, keeping every element: both photos as circles, the name, the role line, the Dog Dad line, and the two social handles with their icons, now as working links. The photos are circles because the source shapes carry prstGeom prst="ellipse", which is easy to miss when only the text is read out of the XML. Assets came from ppt/media, the four images referenced by slide2.xml.rels. The theme underlines every link with a border-bottom, which put a stray rule under both handles, so .intro-social clears it. Mia's crop needs object-position 50% 30%: a portrait centre-cropped to a circle cuts her ears off. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The intro slide now opens on the headshot alone and Mia appears on the first click. v-click alone was not enough: it fades opacity but leaves the element in flow, so Mia's 248px stayed reserved and pushed the headshot off centre in the first state, which read as a mistake rather than a build. Collapsing her with display: none while hidden keeps state one centred and lets the pair re-centre when she lands. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
New order: Nikolay's intro, the nine-page Nexus overview, the hands-on lab slide, then the concept block. The lab slide moves ahead of the concepts because it needs no Nexus vocabulary, so attendees can be opening the sandbox while the terms are still being introduced. Pages 4 to 12 of the [External] Temporal Nexus Overview Slides 2026 deck come in as rendered images. Their diagrams -- the child-workflow and activity-wrapper comparisons, the bespoke gateway, the Nexus lozenge -- are intricate enough that redrawing them in HTML would lose more than it gained, and they are already on brand for a dark deck. Rendered at 160 dpi with PyMuPDF and converted to WebP: 11 MB of PNG became 516 KB for all nine. Each carries a per-page presenter note and its source page. Adds a full-image layout with no footer, since these pages have their own page number and logo baked in and the theme's footer doubled up on both. Its prop is `image`, not `src`, because Slidev reserves `src` in frontmatter for importing slides. Renames "The six words" to "Nexus Fundamentals". The 3 AM failure and the before-and-after slide move to segments/00-parked.md, which slides.md does not import, so nothing renders from it. The overview pages now tell that story in more detail, which makes those two redundant rather than wrong. They are kept intact in case either is wanted back. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
All six were seven-line stubs pointing at the plan; no written content is lost. Removes their files and their src: imports, leaving the deck at 19 slides: the cover plus segment 0. The interleaved per-challenge segments the plan describes are no longer part of the deck. If they come back, the plan and its Mermaid diagram states are still in PR #8. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A Slidev deck for the Kotlin Nexus workshop, opening the hands-on lab in the
nexus-kotlin-decouple-monolithInstruqt track (merged in #9).Only
kotlin/slides/. The original design and plan are #8 — see the note at thebottom, because the deck has moved away from it.
The deck, 19 slides
t.mp/nexus-kotlin, video left, CTA rightThe lab opens before the vocabulary, deliberately: nothing on slide 12 needs a
Nexus term, so attendees can be waiting on the sandbox while the concepts are
still being introduced. Each concept then gets one slide pairing a definition
with real Kotlin from the solution tree.
Registry is deliberately absent from the concept block. The lab names it once in
challenge 2's notes and no challenge ever touches it.
What reviewers may want to weigh in on
The theme is vendored at
kotlin/slides/theme/(~1.1MB, 29 layouts) ratherthan installed as a dependency, and it is most of this PR's line count.
pnpm add github:temporalio/slidev-theme-temporalinstalls fine and then fails the build:pnpm names the store directory for a git dependency with a
#<commit>suffix, andVite's CSS
url()resolver treats#as a fragment separator, so the theme's fivebackground images resolve to a truncated path. Vendoring is Option 2 in the theme's
own README and what the Replay workshop deck does.
Slides 3–11 are rendered images, not rebuilt slides. They come from pages 4–12
of the internal Temporal Nexus Overview 2026 deck. Their diagrams are intricate
enough that redrawing them in HTML would lose more than it gained, and they are
already on brand for a dark deck. Rendered at 160 dpi and converted to WebP: 11MB
of PNG became 516KB for all nine. Consequence to know about: the text is not
editable or searchable, and re-syncing means re-rendering from the source deck.
Two project-local layouts in
kotlin/slides/layouts/, kept out of the vendoredtheme so a theme update cannot clobber them:
code-stack— code above, definition below, both panes auto-shrinking so noslide scrolls or clips. Three CSS traps are documented in comments, each found
by measuring the live DOM rather than reasoning about it.
full-image— one image, no footer. The overview pages carry their own pagenumber and logo, so the theme's footer doubled up on both.
Verification
Drives the dev server with Playwright and reports the settled font size and
overflow for every code slide. It caught two clipping bugs that eyeballing missed.
All six currently report zero overflow.
prettier-plugin-slidevis pinned and configured. Without it a format-on-savepass silently destroys every per-slide frontmatter block: in Markdown a line of
text followed by
---is a setext H2, which is exactly what Slidev frontmatterlooks like.
Still open
Two definitions are unwritten — Handler and sync vs async still carry the
scaffolding note rather than prose.
segments/00-parked.mdholds two superseded slides (the 3 AM failure, and abefore-and-after architecture diagram).
slides.mddoes not import it, so nothingrenders from it.
This deck no longer matches the plan in #8. That plan describes six interleaved
segments, one per challenge; those placeholder files were deleted and the deck is
now a single continuous run into the lab. #8 is worth reading as the record of the
original design, but it should not be treated as a description of this deck.
🤖 Generated with Claude Code