Showing preview only (1,517K chars total). Download the full file or copy to clipboard to get everything.
Repository: slidevjs/slidev
Branch: main
Commit: 3a5f8ddf254c
Files: 649
Total size: 1.3 MB
Directory structure:
gitextract_8zfy5w91/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── config.yml
│ │ └── feature_request.md
│ ├── stale.yml
│ └── workflows/
│ ├── autofix.yml
│ ├── cr.yml
│ ├── release.yml
│ ├── smoke.yml
│ └── test.yml
├── .gitignore
├── .vscode/
│ ├── extensions.json
│ ├── launch.json
│ └── settings.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── cypress/
│ ├── e2e/
│ │ └── examples/
│ │ ├── basic.spec.ts
│ │ ├── presenter-resizer.spec.ts
│ │ └── smoke.spec.ts
│ ├── fixtures/
│ │ └── basic/
│ │ ├── components/
│ │ │ ├── DecorateWithLi.vue
│ │ │ ├── WrapInClicks.vue
│ │ │ ├── WrapInClicksDecorate.vue
│ │ │ └── WrapInComponentInClicks.vue
│ │ ├── global.vue
│ │ ├── package.json
│ │ ├── slides.md
│ │ ├── sub/
│ │ │ ├── page1.md
│ │ │ └── page2.md
│ │ └── vite.config.ts
│ └── tsconfig.json
├── cypress.config.ts
├── demo/
│ ├── README.md
│ ├── composable-vue/
│ │ ├── components/
│ │ │ ├── Connections.vue
│ │ │ ├── DarkToggle.vue
│ │ │ ├── Marker.vue
│ │ │ ├── MarkerCore.vue
│ │ │ ├── MarkerPattern.vue
│ │ │ ├── MarkerTips.vue
│ │ │ ├── NumBox.vue
│ │ │ └── VueUse.vue
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── setup/
│ │ │ └── monaco.ts
│ │ └── slides.md
│ ├── starter/
│ │ ├── README.md
│ │ ├── components/
│ │ │ └── Counter.vue
│ │ ├── package.json
│ │ ├── pages/
│ │ │ └── imported-slides.md
│ │ ├── slides.md
│ │ ├── snippets/
│ │ │ └── external.ts
│ │ ├── style.css
│ │ └── vite.config.ts
│ └── vue-runner/
│ ├── package.json
│ ├── setup/
│ │ ├── code-runners.ts
│ │ └── shiki.ts
│ └── slides.md
├── docs/
│ ├── .gitignore
│ ├── .vitepress/
│ │ ├── addons.ts
│ │ ├── config.ts
│ │ ├── customizations.ts
│ │ ├── pages.ts
│ │ ├── showcases.ts
│ │ ├── sidebar-gen.ts
│ │ ├── theme/
│ │ │ ├── components/
│ │ │ │ ├── AddonGallery.vue
│ │ │ │ ├── AddonInfo.vue
│ │ │ │ ├── Demo.vue
│ │ │ │ ├── DemoEditor.vue
│ │ │ │ ├── DemoSlide.vue
│ │ │ │ ├── Environment.vue
│ │ │ │ ├── FeatureTag.vue
│ │ │ │ ├── FeaturesAnimation.vue
│ │ │ │ ├── FeaturesAnimationInner.vue
│ │ │ │ ├── FeaturesOverview.vue
│ │ │ │ ├── LandingPage.vue
│ │ │ │ ├── Layout.vue
│ │ │ │ ├── LinkCard.vue
│ │ │ │ ├── LinkInline.vue
│ │ │ │ ├── SeeAlso.vue
│ │ │ │ ├── ShowCaseInfo.vue
│ │ │ │ ├── ShowCases.vue
│ │ │ │ ├── SlideContainer.vue
│ │ │ │ ├── TheTweet.vue
│ │ │ │ ├── ThemeGallery.vue
│ │ │ │ └── ThemeInfo.vue
│ │ │ ├── composables/
│ │ │ │ └── dark.ts
│ │ │ ├── index.ts
│ │ │ └── styles/
│ │ │ ├── custom.css
│ │ │ ├── demo.css
│ │ │ └── vars.css
│ │ ├── themes.ts
│ │ └── utils.ts
│ ├── README.md
│ ├── builtin/
│ │ ├── cli.md
│ │ ├── components.md
│ │ └── layouts.md
│ ├── custom/
│ │ ├── config-code-runners.md
│ │ ├── config-context-menu.md
│ │ ├── config-fonts.md
│ │ ├── config-highlighter.md
│ │ ├── config-katex.md
│ │ ├── config-mermaid-renderer.md
│ │ ├── config-mermaid.md
│ │ ├── config-monaco.md
│ │ ├── config-parser.md
│ │ ├── config-routes.md
│ │ ├── config-shortcuts.md
│ │ ├── config-transformers.md
│ │ ├── config-unocss.md
│ │ ├── config-vite.md
│ │ ├── config-vue.md
│ │ ├── directory-structure.md
│ │ └── index.md
│ ├── features/
│ │ ├── block-frontmatter.md
│ │ ├── build-with-pdf.md
│ │ ├── bundle-remote-assets.md
│ │ ├── canvas-size.md
│ │ ├── click-marker.md
│ │ ├── code-block-line-numbers.md
│ │ ├── code-block-max-height.md
│ │ ├── code-groups.md
│ │ ├── comark.md
│ │ ├── direction-variant.md
│ │ ├── draggable.md
│ │ ├── drawing.md
│ │ ├── eject-theme.md
│ │ ├── frontmatter-merging.md
│ │ ├── global-layers.md
│ │ ├── icons.md
│ │ ├── import-snippet.md
│ │ ├── importing-slides.md
│ │ ├── index.data.ts
│ │ ├── index.md
│ │ ├── latex.md
│ │ ├── line-highlighting.md
│ │ ├── mermaid.md
│ │ ├── monaco-editor.md
│ │ ├── monaco-run.md
│ │ ├── monaco-write.md
│ │ ├── notes-auto-ruby.md
│ │ ├── og-image.md
│ │ ├── plantuml.md
│ │ ├── prettier-plugin.md
│ │ ├── recording.md
│ │ ├── remote-access.md
│ │ ├── rough-marker.md
│ │ ├── seo-meta.md
│ │ ├── shiki-magic-move.md
│ │ ├── side-editor.md
│ │ ├── slide-hook.md
│ │ ├── slide-scope-style.md
│ │ ├── slot-sugar.md
│ │ ├── timer.md
│ │ ├── transform-component.md
│ │ ├── twoslash.md
│ │ ├── vscode-extension.md
│ │ └── zoom-slide.md
│ ├── guide/
│ │ ├── animations.md
│ │ ├── component.md
│ │ ├── exporting.md
│ │ ├── faq.md
│ │ ├── global-context.md
│ │ ├── hosting.md
│ │ ├── index.md
│ │ ├── layout.md
│ │ ├── syntax.md
│ │ ├── theme-addon.md
│ │ ├── ui.md
│ │ ├── why.md
│ │ ├── work-with-ai.md
│ │ ├── write-addon.md
│ │ ├── write-layout.md
│ │ └── write-theme.md
│ ├── index.md
│ ├── netlify.toml
│ ├── package.json
│ ├── resources/
│ │ ├── addon-gallery.md
│ │ ├── covers.md
│ │ ├── learning.md
│ │ ├── showcases.md
│ │ └── theme-gallery.md
│ ├── tsconfig.json
│ ├── uno.config.ts
│ └── vite.config.ts
├── eslint.config.js
├── netlify.toml
├── package.json
├── packages/
│ ├── client/
│ │ ├── .generated/
│ │ │ └── unocss-tokens.ts
│ │ ├── App.vue
│ │ ├── README.md
│ │ ├── builtin/
│ │ │ ├── Arrow.vue
│ │ │ ├── AutoFitText.vue
│ │ │ ├── CodeBlockWrapper.vue
│ │ │ ├── CodeGroup.vue
│ │ │ ├── KaTexBlockWrapper.vue
│ │ │ ├── LightOrDark.vue
│ │ │ ├── Link.vue
│ │ │ ├── Mermaid.vue
│ │ │ ├── Monaco.vue
│ │ │ ├── PlantUml.vue
│ │ │ ├── PoweredBySlidev.vue
│ │ │ ├── RenderWhen.vue
│ │ │ ├── ShikiMagicMove.vue
│ │ │ ├── SlideCurrentNo.vue
│ │ │ ├── SlidesTotal.vue
│ │ │ ├── SlidevVideo.vue
│ │ │ ├── Toc.vue
│ │ │ ├── TocList.vue
│ │ │ ├── Transform.vue
│ │ │ ├── Tweet.vue
│ │ │ ├── VAfter.ts
│ │ │ ├── VClick.ts
│ │ │ ├── VClickGap.vue
│ │ │ ├── VClicks.ts
│ │ │ ├── VDrag.vue
│ │ │ ├── VDragArrow.vue
│ │ │ ├── VSwitch.ts
│ │ │ └── Youtube.vue
│ │ ├── composables/
│ │ │ ├── useClicks.ts
│ │ │ ├── useDarkMode.ts
│ │ │ ├── useDragElements.ts
│ │ │ ├── useDrawings.ts
│ │ │ ├── useEmbeddedCtrl.ts
│ │ │ ├── useHideCursorIdle.ts
│ │ │ ├── useIME.ts
│ │ │ ├── useNav.ts
│ │ │ ├── usePreloadImages.ts
│ │ │ ├── usePrintStyles.ts
│ │ │ ├── useSlideBounds.ts
│ │ │ ├── useSlideInfo.ts
│ │ │ ├── useSwipeControls.ts
│ │ │ ├── useTimer.ts
│ │ │ ├── useTocTree.ts
│ │ │ ├── useViewTransition.ts
│ │ │ └── useWakeLock.ts
│ │ ├── constants.ts
│ │ ├── context.ts
│ │ ├── env.ts
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── internals/
│ │ │ ├── Badge.vue
│ │ │ ├── ClicksSlider.vue
│ │ │ ├── CodeRunner.vue
│ │ │ ├── ContextMenu.vue
│ │ │ ├── Controls.vue
│ │ │ ├── CurrentProgressBar.vue
│ │ │ ├── DevicesSelectors.vue
│ │ │ ├── DomElement.vue
│ │ │ ├── DragControl.vue
│ │ │ ├── Draggable.vue
│ │ │ ├── DrawingControls.vue
│ │ │ ├── DrawingLayer.vue
│ │ │ ├── DrawingPreview.vue
│ │ │ ├── ExportPdfTip.vue
│ │ │ ├── FormCheckbox.vue
│ │ │ ├── FormItem.vue
│ │ │ ├── FormSlider.vue
│ │ │ ├── Goto.vue
│ │ │ ├── IconButton.vue
│ │ │ ├── InfoDialog.vue
│ │ │ ├── MenuButton.vue
│ │ │ ├── Modal.vue
│ │ │ ├── NavControls.vue
│ │ │ ├── NoteDisplay.vue
│ │ │ ├── NoteEditable.vue
│ │ │ ├── NoteStatic.vue
│ │ │ ├── PresenterMouse.vue
│ │ │ ├── PrintContainer.vue
│ │ │ ├── PrintSlide.vue
│ │ │ ├── PrintSlideClick.vue
│ │ │ ├── QuickOverview.vue
│ │ │ ├── README.md
│ │ │ ├── RecordingControls.vue
│ │ │ ├── RecordingDialog.vue
│ │ │ ├── ScreenCaptureMirror.vue
│ │ │ ├── SegmentControl.vue
│ │ │ ├── SelectList.vue
│ │ │ ├── Settings.vue
│ │ │ ├── ShadowRoot.vue
│ │ │ ├── ShikiEditor.vue
│ │ │ ├── SideEditor.vue
│ │ │ ├── SlideContainer.vue
│ │ │ ├── SlideLoading.vue
│ │ │ ├── SlideWrapper.vue
│ │ │ ├── SlidesShow.vue
│ │ │ ├── SyncControls.vue
│ │ │ ├── TimerBar.vue
│ │ │ ├── TimerInlined.vue
│ │ │ ├── TitleIcon.vue
│ │ │ ├── VerticalDivider.vue
│ │ │ ├── WebCamera.vue
│ │ │ └── types.ts
│ │ ├── layoutHelper.ts
│ │ ├── layouts/
│ │ │ ├── 404.vue
│ │ │ ├── center.vue
│ │ │ ├── cover.vue
│ │ │ ├── default.vue
│ │ │ ├── end.vue
│ │ │ ├── error.vue
│ │ │ ├── fact.vue
│ │ │ ├── full.vue
│ │ │ ├── iframe-left.vue
│ │ │ ├── iframe-right.vue
│ │ │ ├── iframe.vue
│ │ │ ├── image-left.vue
│ │ │ ├── image-right.vue
│ │ │ ├── image.vue
│ │ │ ├── intro.vue
│ │ │ ├── none.vue
│ │ │ ├── quote.vue
│ │ │ ├── section.vue
│ │ │ ├── statement.vue
│ │ │ ├── two-cols-header.vue
│ │ │ └── two-cols.vue
│ │ ├── logic/
│ │ │ ├── color.ts
│ │ │ ├── contextMenu.ts
│ │ │ ├── dark.ts
│ │ │ ├── overview.ts
│ │ │ ├── recording.ts
│ │ │ ├── route.ts
│ │ │ ├── screenshot.ts
│ │ │ ├── shortcuts.ts
│ │ │ ├── slides.ts
│ │ │ ├── snapshot.ts
│ │ │ ├── transition.ts
│ │ │ └── utils.ts
│ │ ├── main.ts
│ │ ├── modules/
│ │ │ ├── context.ts
│ │ │ ├── mermaid.ts
│ │ │ ├── v-click.ts
│ │ │ ├── v-drag.ts
│ │ │ ├── v-mark.ts
│ │ │ └── v-motion.ts
│ │ ├── package.json
│ │ ├── pages/
│ │ │ ├── 404.vue
│ │ │ ├── entry.vue
│ │ │ ├── export.vue
│ │ │ ├── notes-edit.vue
│ │ │ ├── notes.vue
│ │ │ ├── overview.vue
│ │ │ ├── play.vue
│ │ │ ├── presenter/
│ │ │ │ └── print.vue
│ │ │ ├── presenter.vue
│ │ │ └── print.vue
│ │ ├── scripts/
│ │ │ └── unocss-scan.ts
│ │ ├── setup/
│ │ │ ├── code-runners.ts
│ │ │ ├── context-menu.ts
│ │ │ ├── main.ts
│ │ │ ├── mermaid.ts
│ │ │ ├── monaco.ts
│ │ │ ├── root.ts
│ │ │ ├── routes.ts
│ │ │ ├── shiki-options.ts
│ │ │ ├── shiki.ts
│ │ │ └── shortcuts.ts
│ │ ├── shim-vue.d.ts
│ │ ├── shim.d.ts
│ │ ├── state/
│ │ │ ├── drawings.ts
│ │ │ ├── index.ts
│ │ │ ├── shared.ts
│ │ │ ├── snapshot.ts
│ │ │ ├── storage.ts
│ │ │ └── syncState.ts
│ │ ├── styles/
│ │ │ ├── code.css
│ │ │ ├── index.css
│ │ │ ├── katex.css
│ │ │ ├── layouts-base.css
│ │ │ ├── shiki-twoslash.css
│ │ │ ├── transitions.css
│ │ │ └── vars.css
│ │ ├── uno.config.ts
│ │ └── utils.ts
│ ├── create-app/
│ │ ├── README.md
│ │ ├── build.mjs
│ │ ├── index.mjs
│ │ ├── package.json
│ │ └── template/
│ │ ├── README.md
│ │ ├── _gitignore
│ │ ├── _npmrc
│ │ ├── components/
│ │ │ └── Counter.vue
│ │ ├── netlify.toml
│ │ ├── package.json
│ │ └── vercel.json
│ ├── create-theme/
│ │ ├── README.md
│ │ ├── index.mjs
│ │ ├── package.json
│ │ └── template/
│ │ ├── .vscode/
│ │ │ └── extensions.json
│ │ ├── README.md
│ │ ├── _gitignore
│ │ ├── _npmrc
│ │ ├── components/
│ │ │ └── .gitkeep
│ │ ├── example.md
│ │ ├── layouts/
│ │ │ ├── cover.vue
│ │ │ └── intro.vue
│ │ ├── package.json
│ │ ├── setup/
│ │ │ └── shiki.ts
│ │ └── styles/
│ │ ├── index.ts
│ │ └── layout.css
│ ├── parser/
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── config.ts
│ │ │ ├── core.ts
│ │ │ ├── fs.ts
│ │ │ ├── index.ts
│ │ │ ├── timesplit/
│ │ │ │ ├── index.ts
│ │ │ │ ├── timesplit.test.ts
│ │ │ │ ├── timesplit.ts
│ │ │ │ ├── timestring.test.ts
│ │ │ │ └── timestring.ts
│ │ │ └── utils.ts
│ │ └── tsdown.config.ts
│ ├── slidev/
│ │ ├── LICENSE
│ │ ├── bin/
│ │ │ └── slidev.mjs
│ │ ├── node/
│ │ │ ├── cli.ts
│ │ │ ├── commands/
│ │ │ │ ├── build.ts
│ │ │ │ ├── export.ts
│ │ │ │ ├── serve.ts
│ │ │ │ └── shared.ts
│ │ │ ├── index.ts
│ │ │ ├── integrations/
│ │ │ │ ├── addons.ts
│ │ │ │ ├── drawings.ts
│ │ │ │ ├── snapshots.ts
│ │ │ │ └── themes.ts
│ │ │ ├── options.ts
│ │ │ ├── parser.ts
│ │ │ ├── resolver.test.ts
│ │ │ ├── resolver.ts
│ │ │ ├── setups/
│ │ │ │ ├── indexHtml.ts
│ │ │ │ ├── katex.ts
│ │ │ │ ├── load.ts
│ │ │ │ ├── preparser.ts
│ │ │ │ ├── shiki.ts
│ │ │ │ ├── transformers.ts
│ │ │ │ ├── unocss.ts
│ │ │ │ └── vite-plugins.ts
│ │ │ ├── syntax/
│ │ │ │ ├── markdown-it/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── markdown-it-escape-code.ts
│ │ │ │ │ ├── markdown-it-katex.ts
│ │ │ │ │ ├── markdown-it-link.ts
│ │ │ │ │ ├── markdown-it-shiki.ts
│ │ │ │ │ └── markdown-it-v-drag.ts
│ │ │ │ └── transform/
│ │ │ │ ├── code-wrapper.ts
│ │ │ │ ├── in-page-css.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── katex-wrapper.ts
│ │ │ │ ├── magic-move.ts
│ │ │ │ ├── mermaid.ts
│ │ │ │ ├── monaco.ts
│ │ │ │ ├── plant-uml.ts
│ │ │ │ ├── slot-sugar.ts
│ │ │ │ ├── snippet.ts
│ │ │ │ └── utils.ts
│ │ │ ├── utils.ts
│ │ │ ├── virtual/
│ │ │ │ ├── configs.ts
│ │ │ │ ├── deprecated.ts
│ │ │ │ ├── global-layers.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── layouts.ts
│ │ │ │ ├── monaco-deps.ts
│ │ │ │ ├── monaco-types.ts
│ │ │ │ ├── nav-controls.ts
│ │ │ │ ├── setups.ts
│ │ │ │ ├── slides.ts
│ │ │ │ ├── styles.ts
│ │ │ │ ├── titles.ts
│ │ │ │ └── types.ts
│ │ │ └── vite/
│ │ │ ├── common.ts
│ │ │ ├── compilerFlagsVue.ts
│ │ │ ├── components.ts
│ │ │ ├── contextInjection.ts
│ │ │ ├── extendConfig.ts
│ │ │ ├── hmrPatch.ts
│ │ │ ├── icons.ts
│ │ │ ├── index.ts
│ │ │ ├── inspect.ts
│ │ │ ├── layoutWrapper.ts
│ │ │ ├── loaders.ts
│ │ │ ├── markdown.ts
│ │ │ ├── monacoTypes.ts
│ │ │ ├── monacoWrite.ts
│ │ │ ├── patchMonacoSourceMap.ts
│ │ │ ├── remoteAssets.ts
│ │ │ ├── serverRef.ts
│ │ │ ├── staticCopy.ts
│ │ │ ├── unocss.ts
│ │ │ └── vue.ts
│ │ ├── package.json
│ │ ├── template.md
│ │ ├── tsconfig.json
│ │ └── tsdown.config.ts
│ ├── types/
│ │ ├── README.md
│ │ ├── client.d.ts
│ │ ├── index.d.ts
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── builtin-layouts.ts
│ │ │ ├── cli.ts
│ │ │ ├── clicks.ts
│ │ │ ├── code-runner.ts
│ │ │ ├── config.ts
│ │ │ ├── context-menu.ts
│ │ │ ├── env.ts
│ │ │ ├── frontmatter.ts
│ │ │ ├── hmr.ts
│ │ │ ├── index.ts
│ │ │ ├── options.ts
│ │ │ ├── setups.ts
│ │ │ ├── toc.ts
│ │ │ ├── transform.ts
│ │ │ ├── types.ts
│ │ │ └── vite.ts
│ │ └── tsdown.config.ts
│ └── vscode/
│ ├── .vscodeignore
│ ├── LICENSE
│ ├── README.md
│ ├── language-server/
│ │ ├── bin.ts
│ │ ├── index.ts
│ │ ├── languagePlugin.ts
│ │ ├── prettierService.ts
│ │ ├── protocol.ts
│ │ └── volar-service-yaml.ts
│ ├── package.json
│ ├── schema/
│ │ ├── frontmatter.json
│ │ └── headmatter.json
│ ├── scripts/
│ │ ├── publish.ts
│ │ └── schema.ts
│ ├── src/
│ │ ├── commands.ts
│ │ ├── composables/
│ │ │ ├── useDebouncedComputed.ts
│ │ │ ├── useDevServer.ts
│ │ │ ├── useFocusedSlide.ts
│ │ │ ├── useProjectFromDoc.ts
│ │ │ └── useServerDetector.ts
│ │ ├── configs.ts
│ │ ├── html/
│ │ │ ├── error.ts
│ │ │ └── ready.ts
│ │ ├── index.ts
│ │ ├── languageClient.ts
│ │ ├── lmTools.ts
│ │ ├── projects.ts
│ │ ├── utils/
│ │ │ ├── findPossibleEntries.ts
│ │ │ ├── findShallowestPath.ts
│ │ │ ├── getFirstDisplayedChild.ts
│ │ │ ├── getSlidesTitle.ts
│ │ │ └── toRelativePath.ts
│ │ └── views/
│ │ ├── annotations.ts
│ │ ├── foldings.ts
│ │ ├── logger.ts
│ │ ├── previewWebview.ts
│ │ ├── projectsTree.ts
│ │ └── slidesTree.ts
│ ├── syntaxes/
│ │ ├── .vscode/
│ │ │ └── settings.json
│ │ ├── codeblock-patch.ts
│ │ ├── codeblock.json
│ │ ├── language-configuration.json
│ │ ├── markdown.json
│ │ ├── pages.md
│ │ ├── slidev.example.md
│ │ ├── slidev.tmLanguage.json
│ │ └── tsconfig.json
│ ├── tsconfig.json
│ └── tsdown.config.ts
├── patches/
│ └── @hedgedoc__markdown-it-plugins@2.1.4.patch
├── pnpm-workspace.yaml
├── scripts/
│ ├── demo.mjs
│ ├── gen-layouts.ts
│ ├── pack.mjs
│ ├── publish.mjs
│ ├── remove-overridden-deps.mjs
│ └── update-versions.mjs
├── shim.d.ts
├── skills/
│ ├── GENERATION.md
│ └── slidev/
│ ├── README.md
│ ├── SKILL.md
│ └── references/
│ ├── animation-click-marker.md
│ ├── animation-drawing.md
│ ├── animation-rough-marker.md
│ ├── api-slide-hooks.md
│ ├── build-og-image.md
│ ├── build-pdf.md
│ ├── build-remote-assets.md
│ ├── build-seo-meta.md
│ ├── code-groups.md
│ ├── code-import-snippet.md
│ ├── code-line-highlighting.md
│ ├── code-line-numbers.md
│ ├── code-magic-move.md
│ ├── code-max-height.md
│ ├── code-twoslash.md
│ ├── core-animations.md
│ ├── core-cli.md
│ ├── core-components.md
│ ├── core-exporting.md
│ ├── core-frontmatter.md
│ ├── core-global-context.md
│ ├── core-headmatter.md
│ ├── core-hosting.md
│ ├── core-layouts.md
│ ├── core-syntax.md
│ ├── diagram-latex.md
│ ├── diagram-mermaid.md
│ ├── diagram-plantuml.md
│ ├── editor-monaco-run.md
│ ├── editor-monaco-write.md
│ ├── editor-monaco.md
│ ├── editor-prettier.md
│ ├── editor-side.md
│ ├── editor-vscode.md
│ ├── layout-canvas-size.md
│ ├── layout-draggable.md
│ ├── layout-global-layers.md
│ ├── layout-slots.md
│ ├── layout-transform.md
│ ├── layout-zoom.md
│ ├── presenter-notes-ruby.md
│ ├── presenter-recording.md
│ ├── presenter-remote.md
│ ├── presenter-timer.md
│ ├── style-direction.md
│ ├── style-icons.md
│ ├── style-scoped.md
│ ├── syntax-block-frontmatter.md
│ ├── syntax-frontmatter-merging.md
│ ├── syntax-importing-slides.md
│ ├── syntax-mdc.md
│ └── tool-eject-theme.md
├── taze.config.ts
├── test/
│ ├── __snapshots__/
│ │ ├── parser.test.ts.snap
│ │ ├── transform-all.test.ts.snap
│ │ ├── transform.test.ts.snap
│ │ └── utils.test.ts.snap
│ ├── _tutils.ts
│ ├── fixtures/
│ │ ├── markdown/
│ │ │ ├── frontmatter.md
│ │ │ ├── mdc.md
│ │ │ ├── minimal.md
│ │ │ ├── multi-entries.md
│ │ │ └── sub/
│ │ │ ├── nested1-4.md
│ │ │ ├── page1.md
│ │ │ ├── page2.md
│ │ │ └── pages3-4.md
│ │ └── snippets/
│ │ └── snippet.ts
│ ├── mermaid-renderer.test.ts
│ ├── parser.test.ts
│ ├── transform-all.test.ts
│ ├── transform-magic-move.test.ts
│ ├── transform.test.ts
│ └── utils.test.ts
├── tsconfig.json
├── tsdown.config.ts
└── vitest.config.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
github: antfu
opencollective: slidev
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: "\U0001F41E Bug report"
about: Create a report to help us improve
title: ''
type: Bug
labels: ['pending triage']
assignees: ''
---
<!-- ⚠️ Please DON'T ignore the issue template -->
<!-- 💡 Consider upgrading to the latest version before sending the issue -->
**Describe the bug**
A clear and concise description of what the bug is.
**Minimal reproduction**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See the error
You can use https://sli.dev/new to create a new project to reproduce the issue.
**Environment**
- Slidev version:
- Browser:
- OS:
If you are using Slidev globally (i.e. `npx slidev` or `npm i -g slidev`), please try to reproduce the issue in a local project (i.e. `npm create slidev@latest`).
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
contact_links:
- name: Questions & Discussions
url: https://github.com/slidevjs/slidev/discussions
about: Use GitHub discussions for message-board style questions and discussions.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: "\U00002728 Feature request"
about: Suggest an idea for this project
title: ''
type: Feature
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
================================================
FILE: .github/stale.yml
================================================
daysUntilStale: 60
daysUntilClose: 7
exemptLabels:
- pinned
- security
- no-stale
- no stale
- pr welcome
- help wanted
staleLabel: stale
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
closeComment: false
================================================
FILE: .github/workflows/autofix.yml
================================================
name: autofix.ci
on:
push:
branches:
- main
pull_request:
branches:
- main
permissions:
contents: read
jobs:
autofix:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- name: Use Node.js lts/*
uses: actions/setup-node@v6
with:
node-version: lts/*
- name: Setup
run: npm i -g @antfu/ni
- name: Install
run: nci
env:
CYPRESS_INSTALL_BINARY: 0
- name: Lint
run: nr lint --fix
- uses: autofix-ci/action@dd55f44df8f7cdb7a6bf74c78677eb8acd40cd0a
================================================
FILE: .github/workflows/cr.yml
================================================
# Continuous Releases provided by https://pkg.pr.new
name: CR (Continuous Releases)
on: [push, pull_request]
jobs:
cr:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: lts/*
- run: npm i -g @antfu/ni
- run: nci
- name: Build
run: nr build
- run: nlx pkg-pr-new publish './packages/create-app' './packages/client' './packages/create-theme' './packages/parser' './packages/slidev' './packages/types' --pnpm
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
permissions:
id-token: write
contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
node-version: lts/*
registry-url: https://registry.npmjs.org/
- run: npx changelogithub
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
- run: npm i -g npm@latest
- run: pnpm i
- run: pnpm run ci:publish
- name: Publish to VSCE & OVSX
run: pnpm run publish
working-directory: ./packages/vscode
env:
VSCE_TOKEN: ${{secrets.VSCE_PAT}}
OVSX_TOKEN: ${{secrets.OVSX_PAT}}
================================================
FILE: .github/workflows/smoke.yml
================================================
name: Production Smoke Test
on:
push:
branches:
- main
- master
pull_request:
branches:
- main
- master
workflow_dispatch:
jobs:
pack:
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- name: Set git to use LF
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- uses: actions/checkout@v6
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: lts/*
- name: Setup
run: npm i -g @antfu/ni
- name: Install
run: nci
env:
CYPRESS_INSTALL_BINARY: 0
- name: Build
run: nr build
- name: Pack
run: node ./scripts/pack.mjs /tmp/slidev-pkgs
- name: Upload artifacts
uses: actions/upload-artifact@v6
with:
name: slidev-packages
path: /tmp/slidev-pkgs
test:
needs: pack
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
pm: [npm, pnpm] # yarn not working in this CI
hoist: [true, false]
steps:
- name: Set git to use LF
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- uses: actions/checkout@v6
- name: Use Node.js lts/*
uses: actions/setup-node@v6
with:
node-version: lts/*
- name: Setup
run: npm i -g @antfu/ni
- name: Setup PNPM
uses: pnpm/action-setup@v4
- name: Install
run: nci
- name: Download artifacts
uses: actions/download-artifact@v7
with:
name: slidev-packages
path: /tmp/slidev-pkgs
- name: Create new project
run: |
npm i -g /tmp/slidev-pkgs/create-app.tgz
echo "N" | create-slidev ../temp/slidev-project
- name: Remove npmrc
run: pnpx del-cli ./.npmrc
working-directory: ../temp/slidev-project
if: ${{ ! matrix.hoist }}
- name: Remove overridden dependencies
run: node ${{ github.workspace }}/scripts/remove-overridden-deps.mjs
working-directory: ../temp/slidev-project
- name: Install project (npm, pnpm)
run: ${{ matrix.pm }} i /tmp/slidev-pkgs/cli.tgz playwright-chromium
working-directory: ../temp/slidev-project
if: ${{ matrix.pm != 'yarn' }}
- name: Install Playwright browsers
run: pnpx playwright install chromium
working-directory: ../temp/slidev-project
- name: Install project (yarn)
run: yarn add /tmp/slidev-pkgs/cli.tgz playwright-chromium
working-directory: ../temp/slidev-project
if: ${{ matrix.pm == 'yarn' }}
- name: Test build command in project
run: pnpm build
working-directory: ../temp/slidev-project
- name: E2E Smoke Test
uses: cypress-io/github-action@v7
if: ${{ matrix.os != 'windows-latest' }}
with:
install-command: echo
build: echo
start: pnpm --dir ../temp/slidev-project dev --port 3041
spec: cypress/e2e/examples/smoke.spec.ts
- name: Install globally
run: |
${{ matrix.pm }} i -g /tmp/slidev-pkgs/cli.tgz playwright-chromium
${{ matrix.pm }} i -g @slidev/theme-seriph
if: ${{ matrix.pm != 'yarn' }}
- name: Create slide file
run: pnpm --package=cpy-cli dlx cpy ./packages/slidev/template.md ../temp/ --flat
if: ${{ matrix.pm != 'yarn' }}
- name: Test build command in global mode
run: slidev build template.md
if: ${{ matrix.pm != 'yarn' }}
working-directory: ../temp
# Commented out because it's not working
# - name: E2E test in global mode
# uses: cypress-io/github-action@v7
# if: ${{ matrix.os != 'windows' }}
# with:
# project: ${{ github.workspace }}
# install-command: echo
# build: echo
# start: ${{ 'bash -c "slidev ../template.md"' }}
# spec: cypress/e2e/examples/noError.spec.ts
================================================
FILE: .github/workflows/test.yml
================================================
name: Test
on:
push:
branches:
- main
- master
pull_request:
branches:
- main
- master
jobs:
test:
timeout-minutes: 10
runs-on: ${{ matrix.os }}
strategy:
matrix:
node-version: [lts/*]
os: [ubuntu-latest, windows-latest, macos-latest]
# os: [ubuntu-latest, macos-latest]
fail-fast: false
steps:
- name: Set git to use LF
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- uses: actions/checkout@v6
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
- name: Setup
run: npm i -g @antfu/ni
- name: Install
run: nci
env:
CYPRESS_INSTALL_BINARY: 0
- name: Build
run: nr build
- name: Test
run: nr test
cypress:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: lts/*
- name: Setup
run: npm i -g @antfu/ni
- name: Install
run: nci
- name: Build
run: nr build
- name: Hack Cypress
run: cp pnpm-lock.yaml package-lock.json
- name: Cypress
uses: cypress-io/github-action@v7
with:
install-command: echo
build: nr build
start: nr cy:fixture
================================================
FILE: .gitignore
================================================
.DS_Store
.eslintcache
.idea
.nuxt
.output
.slidev
.vite-inspect
*-export
*.local
*.pdf
.env
assets/demo
components.d.ts
composable-vue-cn
dist
dist-ssr
docs/.vitepress/@slidev
docs/.vitepress/cache
node_modules
cypress/downloads
packages/create-app/template/pages
packages/create-app/template/slides.md
packages/create-app/template/snippets
packages/slidev/README.md
packages/slidev/skills
packages/vscode/syntaxes/codeblock-patch.json
slides-export.md
*slides-export.pptx
.agents
.claude
================================================
FILE: .vscode/extensions.json
================================================
{
"recommendations": [
"antfu.vite",
"Vue.volar",
"antfu.iconify",
"dbaeumer.vscode-eslint",
"antfu.unocss",
"csstools.postcss",
"antfu.pnpm-catalog-lens"
]
}
================================================
FILE: .vscode/launch.json
================================================
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"autoAttachChildProcesses": true,
"args": [
"--extensionDevelopmentPath=${workspaceFolder}/packages/vscode",
"--folder-uri=${workspaceRoot}/packages/vscode/syntaxes"
],
"outFiles": [
"${workspaceFolder}/packages/vscode/dist/**/*.js"
]
}
]
}
================================================
FILE: .vscode/settings.json
================================================
{
"typescript.tsdk": "node_modules/typescript/lib",
"files.associations": {
"*.css": "postcss"
},
"unocss.root": [
"packages/client"
],
// Enable the flat config support
"eslint.experimental.useFlatConfig": true,
// Disable the default formatter
"prettier.enable": false,
"editor.formatOnSave": false,
// Auto fix
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{ "rule": "@stylistic/*", "severity": "off" },
{ "rule": "style*", "severity": "off" },
{ "rule": "*-indent", "severity": "off" },
{ "rule": "*-spacing", "severity": "off" },
{ "rule": "*-spaces", "severity": "off" },
{ "rule": "*-order", "severity": "off" },
{ "rule": "*-dangle", "severity": "off" },
{ "rule": "*-newline", "severity": "off" },
{ "rule": "*quotes", "severity": "off" },
{ "rule": "*semi", "severity": "off" }
],
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"jsonc",
"yaml"
],
"vitest.disableWorkspaceWarning": true,
"slidev.include": [
"**/slides.md",
"packages/vscode/syntax/slidev.example.md"
],
"vue.server.hybridMode": "typeScriptPluginOnly"
}
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
- Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
slidev@antfu.me.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
Excited to hear that you are interested in contributing to this project! Thanks!
## Documentation
Documentation is now being synced from the [`/docs`](https://github.com/slidevjs/slidev/tree/main/docs) folder to the [`slidevjs/docs`](https://github.com/slidevjs/docs) repo.
All Pull Requests for documentation changes should still be made to this repository. Any merged changes will be automatically mirrored to the new documentation repo.
The easiest way to contribute documentation to this project is to follow these steps:
1. [Fork the repository](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo), for example to `https://github.com/octocat/slidev`, where `octocat` is your GitHub username.
2. Clone the newly forked repo from your GitHub account
3. Create a new branch to add your work to, i.e. `git checkout -b docs/update-contributing-guidelines`
4. Make your changes and commit them
5. Push the branch to your fork
6. Go to [https://github.com/slidevjs/slidev/pulls](https://github.com/slidevjs/slidev/pulls), there should be a "Compare & Pull Request" button, where you can create a PR.
## Setup (in your browser)
You can contribute through a development environment in your browser by clicking the following button:
[](https://gitpod.io/#https://github.com/slidevjs/slidev)
## Setup (locally)
This project uses [`pnpm`](https://pnpm.io/) to manage the dependencies, install it if you haven't via
```bash
npm i -g pnpm
```
Clone this repo to your local machine and install the dependencies.
```bash
pnpm install
```
## Development
To build all the packages at once, run the following command on the project root
```bash
pnpm build
```
Build with watch mode
```bash
pnpm dev
```
### Run Demo
To run Slidev locally, you can run
```bash
pnpm demo:dev
```
Or with the real-world example `Composable Vue`:
```bash
pnpm demo:composable-vue
```
The server will restart automatically every time the builds get updated.
## Project Structure
### Monorepo
We use monorepo to manage multiple packages
```
packages
slidev/ - main package entry, holds the code on Node.js side
client/ - main frontend app
parser/ - parser for Slidev's extended Markdown format
create-app/ - scripts and template for `npm init slidev`
create-theme/ - scripts and template for `npm init slidev-theme`
vscode/ - the VSCode extension
```
## Code Style
Don't worry about the code style as long as you install the dev dependencies. Git hooks will format and fix them for you on committing.
## Thanks
Thank you again for being interested in this project! You are awesome!
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020-PRESENT Anthony Fu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<br>
<p align="center">
<a href="https://sli.dev" target="_blank">
<img src="https://sli.dev/logo-title.png" alt="Slidev" height="250" width="250"/>
</a>
</p>
<p align="center">
Presentation <b>slide</b>s for <b>dev</b>elopers 🧑💻👩💻👨💻
</p>
<p align="center">
<a href="https://www.npmjs.com/package/@slidev/cli" target="__blank"><img src="https://img.shields.io/npm/v/@slidev/cli?color=2B90B6&label=" alt="NPM version"></a>
<a href="https://www.npmjs.com/package/@slidev/cli" target="__blank"><img alt="NPM Downloads" src="https://img.shields.io/npm/dm/@slidev/cli?color=349dbe&label="></a>
<a href="https://sli.dev/" target="__blank"><img src="https://img.shields.io/static/v1?label=&message=docs%20%26%20demos&color=45b8cd" alt="Docs & Demos"></a>
<a href="https://sli.dev/resources/theme-gallery" target="__blank"><img src="https://img.shields.io/static/v1?label=&message=themes&color=4ec5d4" alt="Themes"></a>
<br>
<a href="https://github.com/slidevjs/slidev/stargazers" target="__blank"><img alt="GitHub stars" src="https://img.shields.io/github/stars/slidevjs/slidev?style=social"></a>
</p>
<p align="center">
<a href="https://twitter.com/antfu7/status/1389604687502995457">Video Preview</a> | <a href="https://sli.dev">Documentation</a>
</p>
<div align="center">
<table>
<tbody>
<td align="center">
<img width="2000" height="0" alt="" aria-hidden><br>
<sub>Made possible by my <a href="https://github.com/sponsors/antfu">Sponsor Program 💖</a></sub><br>
<img width="2000" height="0" alt="" aria-hidden>
</td>
</tbody>
</table>
</div>
## Features
- 📝 [**Markdown-based**](https://sli.dev/guide/syntax) - focus on content and use your favorite editor
- 🧑💻 [**Developer Friendly**](https://sli.dev/guide/syntax#code-blocks) - built-in code highlighting, live coding, etc.
- 🎨 [**Themable**](https://sli.dev/resources/theme-gallery) - theme can be shared and used with npm packages
- 🌈 [**Stylish**](https://sli.dev/guide/syntax#embedded-styles) - on-demand utilities via [UnoCSS](https://github.com/unocss/unocss).
- 🤹 [**Interactive**](https://sli.dev/custom/directory-structure#components) - embedding Vue components seamlessly
- 🎙 [**Presenter Mode**](https://sli.dev/guide/ui#presenter-mode) - use another window, or even your phone to control your slides
- 🎨 [**Drawing**](https://sli.dev/features/drawing) - draw and annotate on your slides
- 🧮 [**LaTeX**](https://sli.dev/features/latex) - built-in LaTeX math equations support
- 📰 [**Diagrams**](https://sli.dev/guide/syntax#diagrams) - creates diagrams using textual descriptions with [Mermaid](https://mermaid.js.org/)
- 🌟 [**Icons**](https://sli.dev/features/icons) - access to icons from any icon set directly
- 💻 [**Editor**](https://sli.dev/guide/index#editor) - integrated editor, or the [VSCode extension](https://sli.dev/features/vscode-extension)
- 🎥 [**Recording**](https://sli.dev/features/recording) - built-in recording and camera view
- 📤 [**Portable**](https://sli.dev/guide/exporting) - export into PDF, PNGs, or PPTX
- ⚡️ [**Fast**](https://vitejs.dev) - instant reloading powered by [Vite](https://vitejs.dev)
- 🛠 [**Hackable**](https://sli.dev/custom/) - using Vite plugins, Vue components, or any npm packages
## Getting Started
### Try it Online ⚡️
[sli.dev/new](https://sli.dev/new)
[](https://sli.dev/new)
### Init Project Locally
Install [Node.js >=18](https://nodejs.org/) and run the following command:
```bash
npm init slidev
```
Documentation:
**[English](https://sli.dev)** | [中文文档](https://cn.sli.dev) | [Français](https://fr.sli.dev) | [Español](https://es.sli.dev) | [Русский](https://ru.sli.dev) | [Português-BR](https://br.sli.dev)
Discord: [chat.sli.dev](https://chat.sli.dev)
For a full example, you can check the [demo](https://github.com/slidevjs/slidev/blob/main/demo) folder, which is also the source file for [my previous talk](https://antfu.me/posts/composable-vue-vueday-2021).
## Tech Stack
- [Vite](https://vitejs.dev) - An extremely fast frontend tooling
- [Vue 3](https://v3.vuejs.org/) powered [Markdown](https://daringfireball.net/projects/markdown/syntax) - Focus on the content while having the power of HTML and Vue components whenever needed
- [UnoCSS](https://github.com/unocss/unocss) - On-demand utility-first CSS engine, style your slides at ease
- [Shiki](https://github.com/shikijs/shiki), [Monaco Editor](https://github.com/Microsoft/monaco-editor) - First-class code snippets support with live coding capability
- [RecordRTC](https://recordrtc.org) - Built-in recording and camera view
- [VueUse](https://vueuse.org) family - [`@vueuse/core`](https://github.com/vueuse/vueuse), [`@vueuse/motion`](https://github.com/vueuse/motion), etc.
- [Iconify](https://iconify.design/) - Icon sets collection.
- [Drauu](https://github.com/antfu/drauu) - Drawing and annotations support
- [KaTeX](https://katex.org/) - LaTeX math rendering.
- [Mermaid](https://mermaid-js.github.io/mermaid) - Textual Diagrams.
## Sponsors
This project is made possible by all the sponsors supporting my work:
<p align="center">
<a href="https://github.com/sponsors/antfu">
<img src='https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg' alt="Logos from Sponsors" />
</a>
</p>
## License
MIT License © 2021 [Anthony Fu](https://github.com/antfu)
================================================
FILE: cypress/e2e/examples/basic.spec.ts
================================================
export {}
declare global {
// eslint-disable-next-line ts/no-namespace
namespace Cypress {
interface Chainable<Subject> {
rightArrow: (n?: number) => Chainable<Subject>
}
}
}
Cypress.Commands.add('rightArrow', (n = 1) => {
cy.get('body').wait(500).type('{rightarrow}'.repeat(n)).wait(500)
})
const BASE = 'http://localhost:3041'
context('Basic', () => {
beforeEach(() => {
cy.visit('/')
})
function goPage(no: number) {
cy.get('body')
.wait(100)
.type('g')
.wait(100)
.get('#slidev-goto-input')
.type(`${no}`, { force: true })
.type('{enter}', { force: true })
.url()
.should('eq', `${BASE}/${no}`)
.wait(500)
}
it('basic nav', () => {
cy.url()
.should('eq', `${BASE}/1`)
cy.contains('Global Footer')
.should('exist')
cy.get('#page-root > #slide-container > #slide-content')
cy.rightArrow()
.url()
.should('eq', `${BASE}/2`)
cy.contains('Global Footer')
.should('not.exist')
cy.get('#page-root > #slide-container > #slide-content > #slideshow .slidev-page-2 > div > p')
.should('have.css', 'border-color', 'rgb(0, 128, 0)')
.should('not.have.css', 'color', 'rgb(128, 0, 0)')
goPage(5)
cy.get('#page-root > #slide-container > #slide-content > #slideshow .slidev-page-5 .slidev-code')
.should('have.text', '<div>{{$slidev.nav.currentPage}}</div>')
.get('#page-root > #slide-container > #slide-content > #slideshow .slidev-page-5 > div > p')
.should('have.text', 'Current Page: 5')
})
it('should nav correctly', () => {
goPage(5)
cy.get('body')
.type('{DownArrow}')
.url()
.should('eq', `${BASE}/6`)
cy.rightArrow()
cy
.url()
.should('eq', `${BASE}/6?clicks=1`)
cy.get('body')
.type('{RightArrow}{RightArrow}{RightArrow}{RightArrow}{RightArrow}{RightArrow}')
.url()
.should('eq', `${BASE}/7`)
cy.get('body')
.type('{LeftArrow}')
.url()
.should('eq', `${BASE}/6?clicks=6`)
cy.get('body')
.type('{DownArrow}')
.url()
.should('eq', `${BASE}/7`)
cy.get('body')
.type('{UpArrow}')
.url()
.should('eq', `${BASE}/6`)
})
it('named slots', () => {
goPage(8)
cy.get('.col-right')
.contains('Right')
})
it('clicks map', () => {
goPage(9)
cy
.url()
.should('eq', `${BASE}/9`)
cy.rightArrow()
cy
.url()
.should('eq', `${BASE}/9?clicks=1`)
cy.get('#slideshow .slidev-page-9 .cy-content .slidev-vclick-target:not(.slidev-vclick-hidden)')
.should('have.text', 'CD')
cy.rightArrow(2)
cy.get('#slideshow .slidev-page-9 .cy-content .slidev-vclick-target:not(.slidev-vclick-hidden)')
.should('have.text', 'ABCD')
// v-click.hide
cy.rightArrow()
cy.get('#slideshow .slidev-page-9 .cy-content .slidev-vclick-target:not(.slidev-vclick-hidden)')
.should('have.text', 'ABC')
cy
.url()
.should('eq', `${BASE}/9?clicks=4`)
cy.rightArrow()
cy
.url()
.should('eq', `${BASE}/10`)
cy.rightArrow()
cy
.url()
.should('eq', `${BASE}/10?clicks=1`)
cy.get('#slideshow .slidev-page-10 .cy-content-hide .slidev-vclick-target:not(.slidev-vclick-hidden)')
.should('have.text', 'BD')
cy.rightArrow()
cy.get('#slideshow .slidev-page-10 .cy-content-hide .slidev-vclick-target:not(.slidev-vclick-hidden)')
.should('have.text', 'D')
cy.rightArrow()
cy.get('#slideshow .slidev-page-10 .cy-content-hide .slidev-vclick-target:not(.slidev-vclick-hidden)')
.should('have.text', 'CD')
cy.rightArrow()
cy
.url()
.should('eq', `${BASE}/10?clicks=4`)
cy.rightArrow()
cy
.url()
.should('eq', `${BASE}/11`)
})
it('overview nav', () => {
goPage(2)
cy.get('body')
.type('o{RightArrow}{RightArrow}{Enter}')
.url()
.should('eq', `${BASE}/4`)
cy.get('body')
.type('o{LeftArrow}{LeftArrow}{LeftArrow}{Enter}')
.url()
.should('eq', `${BASE}/1`)
cy.get('body')
.type('o{DownArrow}{DownArrow}{DownArrow}{Enter}')
.url()
.should('not.eq', `${BASE}/1`)
cy.get('body')
.type('o{UpArrow}{UpArrow}{UpArrow}{Enter}')
.url()
.should('eq', `${BASE}/1`)
})
it('deep nested lists', () => {
goPage(11)
cy
.url()
.should('eq', `${BASE}/11`)
cy.get('body')
.type('{RightArrow}{RightArrow}{RightArrow}')
cy.get('#slideshow .slidev-page-11 .cy-depth .slidev-vclick-target:not(.slidev-vclick-hidden) .slidev-vclick-target:not(.slidev-vclick-hidden) .slidev-vclick-target:not(.slidev-vclick-hidden)')
.should('have.text', 'C')
cy.get('body')
.type('{RightArrow}{RightArrow}{RightArrow}')
cy.get('#slideshow .slidev-page-11 .cy-depth .slidev-vclick-target:not(.slidev-vclick-hidden) .slidev-vclick-target:not(.slidev-vclick-hidden) .slidev-vclick-target:not(.slidev-vclick-hidden)')
.should('have.text', 'CD')
cy.get('body')
.type('{RightArrow}{RightArrow}{RightArrow}')
cy.get('#slideshow .slidev-page-11 .cy-depth .slidev-vclick-target:not(.slidev-vclick-hidden) .slidev-vclick-target:not(.slidev-vclick-hidden) .slidev-vclick-target:not(.slidev-vclick-hidden)')
.should('have.text', 'CDGH')
cy.get('body')
.type('{RightArrow}{RightArrow}{RightArrow}')
cy.get('#slideshow .slidev-page-11 .cy-depth > ul > .slidev-vclick-target:not(.slidev-vclick-hidden)')
.should('have.text', 'A B CDEF GHIJKL')
})
it('slot in v-clicks', () => {
goPage(12)
cy
.url()
.should('eq', `${BASE}/12`)
cy.get('body')
.type('{RightArrow}{RightArrow}{RightArrow}{RightArrow}{RightArrow}{RightArrow}')
.url()
.should('eq', `${BASE}/12?clicks=6`) // we should still be on page 12
cy.rightArrow()
.url()
.should('eq', `${BASE}/13`)
cy.get('#slideshow .slidev-page-13 .cy-wrapdecorate > ul > .slidev-vclick-target.slidev-vclick-hidden')
.should('have.text', 'AEFZ')
cy.get('body')
.type('{RightArrow}{RightArrow}{RightArrow}')
cy.get('#slideshow .slidev-page-13 .cy-wrapdecorate > ul > .slidev-vclick-target:not(.slidev-vclick-hidden)')
.should('have.text', 'AEF')
cy.rightArrow()
cy.get('#slideshow .slidev-page-13 .cy-wrapdecorate > ul > .slidev-vclick-target:not(.slidev-vclick-hidden)')
.should('have.text', 'AEFZ')
})
})
================================================
FILE: cypress/e2e/examples/presenter-resizer.spec.ts
================================================
const LAYOUT_KEY = 'slidev-presenter-layout'
function visitPresenter(layout: 1 | 2 | 3) {
cy.visit('/presenter', {
onBeforeLoad(win) {
win.localStorage.setItem(LAYOUT_KEY, String(layout))
},
})
}
context('Presenter resizer', () => {
it('shows proper resizer handles per layout', () => {
// Start with a tall viewport so layout 1 is not in wide mode.
cy.viewport(900, 1200)
visitPresenter(1)
cy.get('.note .notes-resizer').should('exist')
cy.get('.note .notes-row-resizer').should('exist')
cy.get('.next .notes-row-resizer').should('not.exist')
cy.get('.notes-vertical-resizer').should('not.exist')
// Switch to wide mode: unified vertical resizer should exist.
cy.viewport(1400, 900)
cy.get('.notes-vertical-resizer').should('exist')
cy.get('.note .notes-resizer').should('not.exist')
visitPresenter(2)
cy.get('.note .notes-resizer').should('exist')
cy.get('.next .notes-row-resizer').should('exist')
cy.get('.note .notes-row-resizer').should('not.exist')
cy.get('.notes-vertical-resizer').should('not.exist')
visitPresenter(3)
cy.get('.note .notes-resizer').should('not.exist')
cy.get('.note .notes-row-resizer').should('exist')
cy.get('.next .notes-row-resizer').should('not.exist')
cy.get('.notes-vertical-resizer').should('not.exist')
cy.get('.notes-vertical-resizer-left').should('exist')
})
it('applies CSS variables for dynamic sizing', () => {
visitPresenter(1)
cy.get('.grid-container')
.invoke('attr', 'style')
.then((style) => {
expect(style).to.include('--slidev-presenter-notes-width')
expect(style).to.include('--slidev-presenter-notes-row-size')
})
})
})
================================================
FILE: cypress/e2e/examples/smoke.spec.ts
================================================
context('Smoke test', () => {
async function testAllSlides() {
while (1) {
let oldUrl, newUrl
cy.get('body')
.url()
.then(url => (oldUrl = url))
.type(`{RightArrow}`)
.wait(1000)
.url()
.then(url => (newUrl = url))
if (oldUrl === newUrl)
break
}
}
it('should throw no error in Play mode', async () => {
cy.visit('/').wait(4000)
await testAllSlides()
})
it('should throw no error in Presenter mode', async () => {
cy.visit('/presenter').wait(4000)
await testAllSlides()
})
it('should throw no error in Overview page', async () => {
cy.visit('/overview').wait(4000)
})
it('should throw no error in Entry page', async () => {
cy.visit('/entry').wait(4000)
})
})
================================================
FILE: cypress/fixtures/basic/components/DecorateWithLi.vue
================================================
<template>
<li>Step b</li>
<slot />
<li>Step y</li>
</template>
================================================
FILE: cypress/fixtures/basic/components/WrapInClicks.vue
================================================
<template>
<div style="border: 1px solid blue">
<v-clicks>
<slot />
</v-clicks>
</div>
</template>
================================================
FILE: cypress/fixtures/basic/components/WrapInClicksDecorate.vue
================================================
<template>
<v-clicks>
<ul style="border: 1px solid red">
<li>A</li>
<slot />
<li>Z</li>
</ul>
</v-clicks>
</template>
================================================
FILE: cypress/fixtures/basic/components/WrapInComponentInClicks.vue
================================================
<template>
<v-clicks>
<ol style="border: 1px solid green">
<li>Point a</li>
<decorate-with-li>
<slot />
</decorate-with-li>
<li>Point z</li>
</ol>
</v-clicks>
</template>
================================================
FILE: cypress/fixtures/basic/global.vue
================================================
<template>
<div
v-if="$slidev.nav.currentPage % 2 === 1"
class="absolute bottom-0 right-0 m-10 hover:text-red-400"
>
Global Footer (appear only on odd page)
</div>
</template>
================================================
FILE: cypress/fixtures/basic/package.json
================================================
{
"private": true,
"scripts": {
"dev": "nodemon -w '../../../packages/slidev/dist/*.js' --exec 'slidev --log=info --port=3041'",
"build": "slidev build",
"export": "slidev export"
},
"devDependencies": {
"@slidev/cli": "workspace:*",
"@slidev/theme-default": "catalog:themes",
"@slidev/theme-seriph": "catalog:themes",
"@slidev/types": "workspace:*",
"nodemon": "catalog:dev"
}
}
================================================
FILE: cypress/fixtures/basic/slides.md
================================================
# Page 1
Hello World
---
# Page 2
```html
<style>
p {
color: red;
}
</style>
```
`<p>` should have a green border, but no red text
<style>
p {
border: 1px solid green;
}
</style>
---
src: sub/page1.md
---
This will be ignored
---
src: sub/page2.md
background: https://sli.dev/demo-cover.png
---
---
# Page 5
```html
<div>{{$slidev.nav.currentPage}}</div>
```
Current Page: {{$slidev.nav.currentPage}}
---
# Page 6
<v-clicks>
- A
- B
- C
</v-clicks>
<v-clicks>
1. C
2. B
3. A
</v-clicks>
---
# Page 7
$$
\begin{aligned}
\frac{D \boldsymbol{v}}{D t}=&-\frac{1}{\rho} \operatorname{grad} p+\frac{\mu}{\rho} \Delta \boldsymbol{v}+\frac{\lambda+\mu}{\rho} \operatorname{grad} \Theta+\frac{\Theta}{\rho} \operatorname{grad}(\lambda+\mu) \\
&+\frac{1}{\rho} \operatorname{grad}(\boldsymbol{v} \cdot \operatorname{grad} \mu)+\frac{1}{\rho} \operatorname{rot}(\boldsymbol{v} \times \operatorname{grad} \mu)-\frac{1}{\rho} \boldsymbol{v} \Delta \mu+\boldsymbol{g}
\end{aligned}
$$
---
layout: two-cols
---
::right::
# Right
<b>Right</b>
:: default ::
# Left
Left
---
# Page 9
<div class="cy-content">
<div v-click="3">A</div>
<div v-click="2">B</div>
<div v-click="1">C</div>
<div v-click.hide="4">D</div>
<v-click hide><div>E</div></v-click>
</div>
---
# Page 10
<div class="cy-content-hide">
<div v-click-hide>A</div>
<div v-click-hide>B</div>
<div v-click>C</div>
<div v-click-hide>D</div>
</div>
---
# Page 11
<div class="cy-depth">
<v-clicks depth="3">
- A
- B
- C
- D
- E
- F
- G
- H
- I
</v-clicks>
<v-clicks>
- J
- K
- L
</v-clicks>
</div>
---
# Page 12
<v-clicks>
<ul><li>A</li><li>B</li></ul>
</v-clicks>
<wrap-in-clicks>
<ul><li>A</li><li>B</li></ul>
</wrap-in-clicks>
<wrap-in-clicks>
- A
- B
</wrap-in-clicks>
---
# Page 13
<div class="cy-wrapdecorate">
<wrap-in-clicks-decorate>
<li>E</li>
<li>F</li>
</wrap-in-clicks-decorate>
(the next is kept for a future patch but not animating the nesting)
<wrap-in-component-in-clicks>
<li>step i</li>
<li>step j</li>
</wrap-in-component-in-clicks>
</div>
================================================
FILE: cypress/fixtures/basic/sub/page1.md
================================================
# Sub page 1
$x+2$
================================================
FILE: cypress/fixtures/basic/sub/page2.md
================================================
---
layout: cover
---
# Sub page 2
<Tweet :id="20"/>
================================================
FILE: cypress/fixtures/basic/vite.config.ts
================================================
import { defineConfig } from 'vite'
export default defineConfig({
build: {
manifest: true,
minify: false,
},
})
================================================
FILE: cypress/tsconfig.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"types": ["cypress"],
"noEmit": true
},
"include": [
"../node_modules/cypress",
"./**/*.ts"
]
}
================================================
FILE: cypress.config.ts
================================================
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
baseUrl: 'http://localhost:3041',
chromeWebSecurity: false,
specPattern: 'cypress/e2e/**/*.spec.*',
supportFile: false,
},
})
================================================
FILE: demo/README.md
================================================
The best way of understanding Slidev is to try it, with the following command:
```bash
npm init slidev
```
Learn more: https://sli.dev
================================================
FILE: demo/composable-vue/components/Connections.vue
================================================
<script setup lang="ts">
import { autoResetRef } from '@vueuse/core'
import { computed, ref, watch } from 'vue'
const a = ref(2)
const b = ref(4)
const a2 = computed(() => a.value ** 2)
const b2 = computed(() => b.value ** 2)
const c = computed(() => a2.value + b2.value)
const a2c = autoResetRef(false, 100)
const b2c = autoResetRef(false, 100)
const cc = autoResetRef(false, 100)
watch(a, () => a2c.value = true)
watch(b, () => b2c.value = true)
watch([a2, b2], () => cc.value = true)
</script>
<template>
<div>
<div class="px-6 pt-4 text-xl">
<b>𝒛=𝒙²+𝒚²</b>={{ a }}x{{ a }}+{{ b }}x{{ b }}={{ c }}
</div>
<div class="relative w-100 h-100">
<NumBox
v-model:value="a"
label="𝒙"
class="absolute left-5 top-5 from-green-400 to-cyan-500"
:controls="true"
/>
<NumBox
v-model:value="b"
label="𝒚"
class="absolute left-5 top-30 from-green-400 to-cyan-500"
:controls="true"
/>
<NumBox
:value="a2"
label="𝒙²"
class="absolute left-35 top-5 from-blue-400 to-purple-400"
:active="a2c"
/>
<NumBox
:value="b2"
label="𝒚²"
class="absolute left-35 top-30 from-blue-400 to-purple-400"
:active="b2c"
/>
<NumBox
:value="c"
label="𝒛"
class="absolute left-65 top-17.5 from-blue-400 to-purple-400"
:active="cc"
/>
<!-- Line1 -->
<svg
class="absolute left-20 top-10 -z-1"
width="62"
height="1"
viewBox="0 0 62 1"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<line
x1="-3.75678e-10"
y1="0.5"
x2="62"
y2="0.5"
stroke="#888888"
stroke-dasharray="5 2"
/>
</svg>
<!-- Line2 -->
<svg
class="absolute left-20 top-40 -z-1"
width="62"
height="1"
viewBox="0 0 62 1"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<line
x1="-3.75678e-10"
y1="0.5"
x2="62"
y2="0.5"
stroke="#888888"
stroke-dasharray="5 2"
/>
</svg>
<!-- Arc1 -->
<svg
class="absolute left-50 top-10 -z-1"
width="62"
height="61"
viewBox="0 0 62 61"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M0 0.5C27.5 0.5 39.5 59 61.5 60" stroke="#888888" stroke-dasharray="5 2" />
</svg>
<!-- Arc2 -->
<svg
class="absolute left-50 top-25 -z-1"
width="62"
height="61"
viewBox="0 0 62 61"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M0 60.5C27.5 60.5 39.5 2 61.5 1" stroke="#888888" stroke-dasharray="5 2" />
</svg>
</div>
</div>
</template>
================================================
FILE: demo/composable-vue/components/DarkToggle.vue
================================================
<script setup lang="ts">
import { useDarkMode } from '@slidev/client'
const { isDark, toggleDark } = useDarkMode()
</script>
<template>
<button
class="bg-primary rounded border-b-2 border-green-900 text-sm px-2 pt-1.5 pb-1 inline-block !outline-none hover:bg-opacity-85"
@click="toggleDark()"
>
<div class="flex">
<div v-if="isDark" class="i-carbon:moon" />
<div v-else class="i-carbon:sun" />
<span class="mr-1 ml-2">{{ isDark ? 'Dark' : 'Light' }}</span>
</div>
</button>
</template>
================================================
FILE: demo/composable-vue/components/Marker.vue
================================================
<template>
<span class="bg-$slidev-code-background px-2 py-1 rounded font-mono inline-block text-0.4em line-height-1em transform translate-y-[-1.5em] translate-x-[-0.3em]"><slot /></span>
</template>
================================================
FILE: demo/composable-vue/components/MarkerCore.vue
================================================
<template>
<Marker class="text-green-500">
Core
</Marker>
</template>
================================================
FILE: demo/composable-vue/components/MarkerPattern.vue
================================================
<template>
<Marker class="text-pink-500">
Pattern
</Marker>
</template>
================================================
FILE: demo/composable-vue/components/MarkerTips.vue
================================================
<template>
<Marker class="text-orange-400">
Tips
</Marker>
</template>
================================================
FILE: demo/composable-vue/components/NumBox.vue
================================================
<script setup lang="ts">
import { useVModel } from '@vueuse/core'
const props = defineProps<{
value: number
label?: string
active?: boolean
controls?: boolean
}>()
const emit = defineEmits<{
(e: any): void
}>()
const num = useVModel(props, 'value', emit)
</script>
<template>
<div
class="w-16 h-16 rounded-xl text-white overflow-hidden"
style="background-image: radial-gradient(farthest-corner at 0 0, var(--un-gradient-from) 30%, var(--un-gradient-to))"
>
<div
style="background-image: radial-gradient(farthest-corner at 0 0, var(--un-gradient-from) 30%, var(--un-gradient-to))"
class="absolute w-full h-full from-orange-400 to-red-400 opacity-0 transition duration-400 ease-in pointer-events-none"
:class="active ? '!opacity-100 !duration-10' : ''"
/>
<slot>
<div class="absolute left-0 top-0 px-2 py-0.5 opacity-40">
{{ label }}
</div>
<div class="absolute w-full h-full flex z-10">
<div class="m-auto text-2xl leading-0.8em" :class="controls ? 'pl-2' : ''">
{{ value }}
</div>
<div v-if="controls" class="grid grid-rows-2 text-xl">
<mdi:menu-up-outline
class="mt-auto opacity-50 hover:opacity-100 pr-1 cursor-pointer"
@click="num += 1"
/>
<mdi:menu-down-outline
class="opacity-50 hover:opacity-100 pr-1 cursor-pointer"
@click="num -= 1"
/>
</div>
</div>
</slot>
</div>
</template>
================================================
FILE: demo/composable-vue/components/VueUse.vue
================================================
<script setup lang="ts">
defineProps<{
name: string
}>()
</script>
<template>
<div class="px-2 -mx-2 mt-4 py-2">
<img src="https://vueuse.org/favicon.svg" class="h-1em -mb-0.5 mr-2 inline-block align-baseline" alt="">
<span class="opacity-50">Available in VueUse: </span><a class="font-mono opacity-75 hover:opacity-100" :href="`https://vueuse.org/${name}`">{{ name }}</a>
</div>
</template>
================================================
FILE: demo/composable-vue/index.html
================================================
<head>
<link rel="icon" type="image/svg" href="https://antfu.me/favicon.svg" />
</head>
================================================
FILE: demo/composable-vue/package.json
================================================
{
"private": true,
"scripts": {
"dev": "nodemon -w '../../packages/slidev/dist/*.mjs' --exec 'slidev --log=info'",
"build": "slidev build",
"export": "slidev export",
"export:clicks": "slidev export --with-clicks"
},
"devDependencies": {
"@iconify-json/mdi": "catalog:icons",
"@iconify-json/ri": "catalog:icons",
"@slidev/cli": "workspace:*",
"@slidev/theme-default": "catalog:themes",
"@slidev/theme-seriph": "catalog:themes",
"@slidev/types": "workspace:*",
"nodemon": "catalog:dev"
}
}
================================================
FILE: demo/composable-vue/setup/monaco.ts
================================================
import { defineMonacoSetup } from '@slidev/types'
export default defineMonacoSetup((monaco) => {
monaco.languages.typescript.typescriptDefaults.addExtraLib(
`
import { InjectionKey } from 'vue'
export interface UserInfo { id: number; name: string }
export const injectKeyUser: InjectionKey<UserInfo> = Symbol()
`,
'file:///root/context.ts',
)
})
================================================
FILE: demo/composable-vue/slides.md
================================================
---
layout: cover
download: 'https://antfu.me/talks/2021-04-29'
highlighter: shiki
monaco: true
info: |
## Composable Vue
Patterns and tips for writing good composable logic in Vue
[Anthony Fu](https://antfu.me/) at [VueDay 2021](https://2021.vueday.it/)
- [Recording](https://www.youtube.com/watch?v=IMJjP6edHd0)
- [Transcript](https://antfu.me/posts/composable-vue-vueday-2021)
- [Source code](https://github.com/antfu/talks/tree/master/2021-04-29)
---
# Composable Vue
Patterns and tips for writing good composable logic in Vue
<div class="uppercase text-sm tracking-widest">
Anthony Fu
</div>
<div class="abs-bl mx-14 my-12 flex">
<img src="https://2020.vueday.it/img/themes/vueday/vueday-logo.png" class="h-8" alt="">
<div class="ml-3 flex flex-col text-left">
<div><b>Vue</b>Day</div>
<div class="text-sm opacity-50">Apr. 29th, 2021</div>
</div>
</div>
---
layout: 'intro'
---
# Anthony Fu
<div class="leading-8 opacity-80">
Vue core team member and Vite team member.<br>
Creator of VueUse, i18n Ally and Type Challenges.<br>
A fanatical full-time open sourceror.<br>
</div>
<div class="my-10 grid grid-cols-[40px_1fr] w-min gap-y-4">
<ri-github-line class="opacity-50"/>
<div><a href="https://github.com/antfu" target="_blank">antfu</a></div>
<ri-twitter-line class="opacity-50"/>
<div><a href="https://twitter.com/antfu7" target="_blank">antfu7</a></div>
<ri-user-3-line class="opacity-50"/>
<div><a href="https://antfu.me" target="_blank">antfu.me</a></div>
</div>
<img src="https://antfu.me/avatar.png" class="rounded-full w-40 abs-tr mt-16 mr-12" alt="A comic art image of Anthony Fu"/>
---
name: Sponsors
layout: center
---
<img class="h-100 -mt-10" src="https://cdn.jsdelivr.net/gh/antfu/static/sponsors.png" alt="A list of the sponsor logos" /><br>
<div class="text-center text-xs opacity-50 -mt-8 hover:opacity-100">
<a href="https://github.com/sponsors/antfu" target="_blank">
Sponsor me at GitHub
</a>
</div>
---
layout: center
---
# Composable Vue
---
name: VueUse
layout: center
---
<div class="grid grid-cols-[3fr_2fr] gap-4">
<div class="text-center pb-4">
<img class="h-50 inline-block" src="https://vueuse.org/favicon.svg" alt="">
<div class="opacity-50 mb-2 text-sm">
Collection of essential Vue Composition Utilities
</div>
<div class="text-center">
<a class="!border-none" href="https://www.npmjs.com/package/@vueuse/core" target="__blank"><img class="h-4 inline mx-0.5" src="https://img.shields.io/npm/v/@vueuse/core?color=a1b858&label=" alt="NPM version"></a>
<a class="!border-none" href="https://www.npmjs.com/package/@vueuse/core" target="__blank"><img class="h-4 inline mx-0.5" alt="NPM Downloads" src="https://img.shields.io/npm/dm/@vueuse/core?color=50a36f&label="></a>
<a class="!border-none" href="https://vueuse.org" target="__blank"><img class="h-4 inline mx-0.5" src="https://img.shields.io/static/v1?label=&message=docs%20%26%20demos&color=1e8a7a" alt="Docs & Demos"></a>
<img class="h-4 inline mx-0.5" alt="Function Count" src="https://img.shields.io/badge/-114%20functions-13708a">
<br>
<a class="!border-none" href="https://github.com/vueuse/vueuse" target="__blank"><img class="mt-2 h-4 inline mx-0.5" alt="GitHub stars" src="https://img.shields.io/github/stars/vueuse/vueuse?style=social"></a>
</div>
</div>
<div class="border-l border-main !all:leading-12 !all:list-none my-auto">
- Works for both Vue 2 and 3
- Tree-shakeable ESM
- CDN compatible
- TypeScript
- Rich ecosystems
</div>
</div>
---
layout: center
class: text-center
---
# Composition API
a brief go-through
---
<div class="grid grid-cols-2 gap-x-4"><div>
# Ref
```ts {monaco}
import { ref } from 'vue'
let foo = 0
let bar = ref(0)
foo = 1
bar = 1 // ts-error
```
<div v-click>
### Pros
- More explicit, with type checking
- Less caveats
### Cons
- `.value`
</div>
</div><div>
# Reactive
```ts {monaco}
import { reactive } from 'vue'
const foo = { prop: 0 }
const bar = reactive({ prop: 0 })
foo.prop = 1
bar.prop = 1
```
<div v-click>
### Pros
- Auto unwrapping (a.k.a `.value` free)
### Cons
- Same as plain objects on types
- Destructure loses reactivity
- Need to use callback for `watch`
</div>
</div></div>
---
# Ref Auto Unwrapping <MarkerCore />
Get rid of `.value` for most of the time.
<div class="grid grid-cols-2 gap-x-4">
<v-clicks :every='2'>
- `watch` accepts ref as the watch target, and returns the unwrapped value in the callback
```ts
const counter = ref(0)
watch(counter, (count) => {
console.log(count) // same as `counter.value`
})
```
- Ref is auto unwrapped in the template
```html
<template>
<button @click="counter += 1">
Counter is {{ counter }}
</button>
</template>
```
- Reactive will auto-unwrap nested refs.
<div>
```ts {monaco}
import { reactive, ref } from 'vue'
const foo = ref('bar')
const data = reactive({ foo, id: 10 })
data.foo // 'bar'
```
</div>
</v-clicks>
</div>
---
# `unref` - Opposite of Ref <MarkerCore />
- If it gets a Ref, returns the value of it.
- Otherwise, returns as-is.
<div class="grid grid-cols-2 gap-x-4 mt-4">
<div v-click>
### Implementation
```ts
function unref<T>(r: Ref<T> | T): T {
return isRef(r) ? r.value : r
}
```
</div><div v-click>
### Usage
```ts {monaco}
import { ref, unref } from 'vue'
const foo = ref('foo')
unref(foo) // 'foo'
const bar = 'bar'
unref(bar) // 'bar'
```
</div></div>
---
layout: center
class: text-center
---
# Patterns & Tips
of writing composable functions
---
# What's Composable Functions
Sets of reusable logic, separation of concerns.
<div v-click class="grid grid-cols-[1fr_130px]">
```ts
export function useDark(options: UseDarkOptions = {}) {
const preferredDark = usePreferredDark() // <--
const store = useLocalStorage('vueuse-dark', 'auto') // <--
return computed<boolean>({
get() {
return store.value === 'auto'
? preferredDark.value
: store.value === 'dark'
},
set(v) {
store.value = v === preferredDark.value
? 'auto'
: v ? 'dark' : 'light'
},
})
}
```
<div class="grid">
<DarkToggle class="m-auto"/>
</div>
</div>
<div v-click class="abs-b mx-14 my-12">
<VueUse name="useDark"/>
</div>
---
# Think as "Connections"
The `setup()` only runs **once** on component initialization, to construct the relations between your state and logic.
- Input → Output<sup class="ml-1 opacity-50">Effects</sup>
- Output reflects to input's changes automatically
<div class="grid grid-cols-[auto_1fr] gap-4">
<Connections v-click class="mt-4"/>
<div v-click class="p-4">
<h3 class="pb-2">SpreadSheet Formula</h3>
<img class="h-40" src="https://cdn.wallstreetmojo.com/wp-content/uploads/2019/01/Division-Formula-in-Excel-Example-1-1.png" alt="">
</div>
</div>
---
# One Thing at a Time
Just the same as authoring JavaScript functions.
- Extract duplicated logics into composable functions
- Have meaningful names
- Consistent naming conversions - `useXX` `createXX` `onXX`
- Keep function small and simple
- "Do one thing, and do it well"
---
# Passing Refs as Arguments <MarkerPattern />
<div class="grid grid-cols-[160px_1fr_180px] gap-x-4">
<div />
### Implementation
### Usage
<v-clicks :every='3'>
<div class="my-auto leading-6 text-base opacity-75">
Plain function
</div>
```ts
function add(a: number, b: number) {
return a + b
}
```
```ts
const a = 1
const b = 2
const c = add(a, b) // 3
```
<div class="my-auto leading-6 text-base opacity-75">
Accepts refs,<br>
returns a reactive result.
</div>
```ts
function add(a: Ref<number>, b: Ref<number>) {
return computed(() => a.value + b.value)
}
```
```ts
const a = ref(1)
const b = ref(2)
const c = add(a, b)
c.value // 3
```
<div class="my-auto leading-6 text-base opacity-75">
Accepts both refs and plain values.
</div>
```ts
function add(
a: Ref<number> | number,
b: Ref<number> | number,
) {
return computed(() => unref(a) + unref(b))
}
```
```ts
const a = ref(1)
const c = add(a, 5)
c.value // 6
```
</v-clicks>
</div>
---
# MaybeRef <MarkerTips/>
A custom type helper
```ts
type MaybeRef<T> = Ref<T> | T
```
<v-click>
In VueUse, we use this helper heavily to support optional reactive arguments
```ts
export function useTimeAgo(
time: Date | number | string | Ref<Date | number | string>,
) {
return computed(() => someFormating(unref(time)))
}
```
```ts {monaco}
import type { Ref } from 'vue'
import { computed, unref } from 'vue'
type MaybeRef<T> = Ref<T> | T
export function useTimeAgo(
time: MaybeRef<Date | number | string>,
) {
return computed(() => someFormating(unref(time)))
}
```
</v-click>
---
# Make it Flexible <MarkerPattern />
Make your functions like LEGO, can be used with different components in different ways.
<div class="grid grid-cols-2 gap-x-4">
<div v-click>
### Create a "Special" Ref
```ts {monaco}
import { useTitle } from '@vueuse/core'
const title = useTitle()
title.value = 'Hello World'
// now the page's title changed
```
</div><div v-click>
### Binding an Existing Ref
```ts {monaco}
import { useTitle } from '@vueuse/core'
import { computed, ref } from 'vue'
const name = ref('Hello')
const title = computed(() => {
return `${name.value} - World`
})
useTitle(title) // Hello - World
name.value = 'Hi' // Hi - World
```
</div></div>
<div v-click class="abs-b mx-14 my-12">
<VueUse name="useTitle"/>
</div>
---
# `useTitle` <Marker class="text-blue-400">Case</Marker>
Take a look at `useTitle`'s implementation
<div class="grid grid-cols-2 gap-4">
<v-clicks>
```ts {monaco}
import type { MaybeRef } from '@vueuse/core'
import { ref, watch } from 'vue'
export function useTitle(
newTitle: MaybeRef<string | null | undefined>,
) {
const title = ref(newTitle || document.title)
watch(title, (t) => {
if (t != null)
document.title = t
}, { immediate: true })
return title
}
```
```html
<-- 1. use the user provided ref or create a new one
<-- 2. sync ref changes to the document title
```
</v-clicks>
</div>
---
# "Reuse" Ref <MarkerCore />
<v-clicks>
If you pass a `ref` into `ref()`, it will return the original ref as-is.
```ts
const foo = ref(1) // Ref<1>
const bar = ref(foo) // Ref<1>
foo === bar // true
```
```ts
function useFoo(foo: Ref<string> | string) {
// no need!
const bar = isRef(foo) ? foo : ref(foo)
// they are the same
const bar = ref(foo)
/* ... */
}
```
Extremely useful in composable functions that take uncertain argument types.
</v-clicks>
---
# `ref` / `unref` <MarkerTips />
<div v-click>
- `MaybeRef<T>` works well with `ref` and `unref`.
- Use `ref()` when you want to normalized it as a Ref.
- Use `unref()` when you want to have the value.
<br>
```ts
type MaybeRef<T> = Ref<T> | T
function useBala<T>(arg: MaybeRef<T>) {
const reference = ref(arg) // get the ref
const value = unref(arg) // get the value
}
```
</div>
---
# Object of Refs <MarkerPattern />
Getting benefits from both `ref` and `reactive` for authoring composable functions
<div class="mt-1" />
<div class="grid grid-cols-2 gap-x-4">
<v-clicks>
```ts {monaco}
import { reactive, ref } from 'vue'
function useMouse() {
return {
x: ref(0),
y: ref(0),
}
}
const { x, y } = useMouse()
const mouse = reactive(useMouse())
mouse.x === x.value // true
```
<div class="px-2 py-4">
- Destructurable as Ref
- Convert to reactive object to get the auto-unwrapping when needed
</div>
</v-clicks>
</div>
---
# Async to "Sync" <MarkerTips />
With Composition API, we can actually turn async data into "sync"
<div v-click>
### Async
```ts
const data = await fetch('https://api.github.com/').then(r => r.json())
// use data
```
</div>
<div v-click>
### Composition API
```ts
const { data } = useFetch('https://api.github.com/').json()
const user_url = computed(() => data.value?.user_url)
```
</div>
<div v-click>
Establish the "Connections" first, then wait for data to be filled up. The idea is similar to SWR (stale-while-revalidate)
</div>
---
# `useFetch` <Marker class="text-blue-400">Case</Marker>
<v-click>
```ts
export function useFetch<R>(url: MaybeRef<string>) {
const data = shallowRef<T | undefined>()
const error = shallowRef<Error | undefined>()
fetch(unref(url))
.then(r => r.json())
.then(r => data.value = r)
.catch(e => error.value = e)
return {
data,
error,
}
}
```
</v-click>
<div v-click class="abs-b mx-14 my-12">
<VueUse name="useFetch"/>
</div>
---
# Side-effects Self Cleanup <MarkerPattern />
The `watch` and `computed` will stop themselves on components unmounted.<br>
We'd recommend following the same pattern for your custom composable functions.
<div v-click>
```ts {monaco}
import { onUnmounted } from 'vue'
export function useEventListener(target: EventTarget, name: string, fn: any) {
target.addEventListener(name, fn)
onUnmounted(() => {
target.removeEventListener(name, fn) // <--
})
}
```
</div>
<div v-click class="abs-b mx-14 my-12">
<VueUse name="useEventListener"/>
</div>
<!--
Lower the mental burden
-->
---
# `effectScope` RFC <Marker class="text-purple-400">Upcoming</Marker>
A new API to collect the side effects automatically. Likely to be shipped with Vue 3.1<br>
https://github.com/vuejs/rfcs/pull/212
```ts
// effect, computed, watch, watchEffect created inside the scope will be collected
const scope = effectScope(() => {
const doubled = computed(() => counter.value * 2)
watch(doubled, () => console.log(double.value))
watchEffect(() => console.log('Count: ', double.value))
})
// dispose all effects in the scope
stop(scope)
```
---
disabled: true
---
# Template Ref <MarkerTips />
To get DOM element, you can pass a ref to it, and it will be available after component mounted
<div v-click>
```ts {monaco}
import { defineComponent, onMounted, ref } from 'vue'
export default defineComponent({
setup() {
const element = ref<HTMLElement | undefined>()
onMounted(() => {
element.value // now you have it
})
return { element }
},
})
```
```html {monaco}
<template>
<div ref="element"><!-- ... --></div>
</template>
```
</div>
---
disabled: true
---
# Template Ref <MarkerTips />
Use `watch` instead of `onMounted` to unify the handling for template ref changes.
<div>
<v-click>
```ts {monaco}
import { defineComponent, ref, watch } from 'vue'
export default defineComponent({
setup() {
const element = ref<HTMLElement | undefined>()
watch(element, (el) => {
// clean up previous side effect
if (el) {
// use the DOM element
}
})
return { element }
},
})
```
</v-click>
</div>
---
# Typed Provide / Inject <MarkerCore/>
Use the `InjectionKey<T>` helper from Vue to share types across context.
<div v-click>
```ts {monaco}
// context.ts
import type { InjectionKey } from 'vue'
export interface UserInfo {
id: number
name: string
}
export const injectKeyUser: InjectionKey<UserInfo> = Symbol('user')
```
</div>
---
# Typed Provide / Inject <MarkerCore/>
Import the key from the same module for `provide` and `inject`.
<div class="grid grid-cols-2 gap-4">
<v-clicks>
```ts {monaco}
// parent.vue
import { provide } from 'vue'
import { injectKeyUser } from './context'
export default {
setup() {
provide(injectKeyUser, {
id: '7', // type error: should be number
name: 'Anthony',
})
},
}
```
```ts {monaco}
// child.vue
import { inject } from 'vue'
import { injectKeyUser } from './context'
export default {
setup() {
const user = inject(injectKeyUser)
// UserInfo | undefined
if (user)
console.log(user.name) // Anthony
},
}
```
</v-clicks>
</div>
---
# Shared State <MarkerPattern />
By the nature of Composition API, states can be created and used independently.
<div class="grid grid-cols-2 gap-4">
<v-click>
```ts
// shared.ts
import { reactive } from 'vue'
export const state = reactive({
foo: 1,
bar: 'Hello',
})
```
</v-click>
<div>
<v-clicks>
```ts
// A.vue
import { state } from './shared.ts'
state.foo += 1
```
```ts
// B.vue
import { state } from './shared.ts'
console.log(state.foo) // 2
```
</v-clicks>
</div>
</div>
<h3 v-click class="opacity-100">⚠️ But it's not SSR compatible!</h3>
---
# Shared State (SSR friendly) <MarkerPattern />
Use `provide` and `inject` to share the app-level state
<div class="grid grid-cols-[max-content_1fr] gap-4">
<v-click>
```ts
export const myStateKey: InjectionKey<MyState> = Symbol('state')
export function createMyState() {
const state = {
/* ... */
}
return {
install(app: App) {
app.provide(myStateKey, state)
},
}
}
export function useMyState(): MyState {
return inject(myStateKey)!
}
```
</v-click>
<div>
<v-clicks>
```ts
// main.ts
const App = createApp(App)
app.use(createMyState())
```
```ts
// A.vue
// use everywhere in your app
const state = useMyState()
```
<div class="my-3">
- [Vue Router v4](https://github.com/vuejs/vue-router-next) is using the similar approach
</div>
</v-clicks>
</div>
</div>
---
# useVModel <MarkerTips />
A helper to make props/emit easier
<div class="grid grid-cols-2 gap-4">
<v-click>
```ts
export function useVModel(props, name) {
const emit = getCurrentInstance().emit
return computed({
get() {
return props[name]
},
set(v) {
emit(`update:${name}`, v)
},
})
}
```
</v-click>
<div>
<v-click>
```ts
export default defineComponent({
setup(props) {
const value = useVModel(props, 'value')
return { value }
},
})
```
</v-click>
<br>
<v-click>
```html
<template>
<input v-model="value" />
</template>
```
</v-click>
</div>
</div>
<div v-click class="abs-b mx-14 my-12">
<VueUse name="useVModel"/>
</div>
---
disabled: true
---
# useVModel (Passive) <MarkerTips />
Make the model able to be updated **independently** from the parent logic
<v-click>
```ts
export function usePassiveVModel(props, name) {
const emit = getCurrentInstance().emit
const data = ref(props[name]) // store the value in a ref
watch(() => props.value, v => data.value = v) // sync the ref whenever the prop changes
return computed({
get() {
return data.value
},
set(v) {
data.value = v // when setting value, update the ref directly
emit(`update:${name}`, v) // then emit out the changes
},
})
}
```
</v-click>
---
layout: center
---
# All of them work for both Vue 2 and 3
---
# `@vue/composition-api` <Marker class="text-teal-400">Lib</Marker>
Composition API support for Vue 2.<br><carbon-logo-github class="inline-block"/> [vuejs/composition-api](https://github.com/vuejs/composition-api)
```ts
import VueCompositionAPI from '@vue/composition-api'
import Vue from 'vue'
Vue.use(VueCompositionAPI)
```
```ts
import { reactive, ref } from '@vue/composition-api'
```
---
# Vue 2.7 <Marker class="text-purple-400">Upcoming</Marker>
[Plans in Vue 2.7](https://github.com/vuejs/rfcs/blob/ie11/active-rfcs/0000-vue3-ie11-support.md#for-those-who-absolutely-need-ie11-support)
- Backport `@vue/composition-api` into Vue 2's core.
- `<script setup>` syntax in Single-File Components.
- Migrate codebase to TypeScript.
- IE11 support.
- LTS.
---
# Vue Demi <Marker class="text-teal-400">Lib</Marker>
Creates Universal Library for Vue 2 & 3<br><carbon-logo-github class="inline-block"/> [vueuse/vue-demi](https://github.com/vueuse/vue-demi)
```ts
// same syntax for both Vue 2 and 3
import { defineComponent, reactive, ref } from 'vue-demi'
```
<img class="h-50 mx-auto" src="https://cdn.jsdelivr.net/gh/vueuse/vue-demi/assets/banner.png" alt="" />
---
# Recap
- Think as "Connections"
- One thing at a time
- Accepting ref as arguments
- Returns an object of refs
- Make functions flexible
- Async to "sync"
- Side-effect self clean up
- Shared state
---
layout: center
class: 'text-center pb-5 :'
---
# Thank You!
Slides can be found on [antfu.me](https://antfu.me)
================================================
FILE: demo/starter/README.md
================================================
See [../../packages/create-app/template/README.md](../../packages/create-app/template/README.md)
================================================
FILE: demo/starter/components/Counter.vue
================================================
<script setup lang="ts">
import { ref } from 'vue'
const props = defineProps({
count: {
default: 0,
},
})
const counter = ref(props.count)
</script>
<template>
<div flex="~" w="min" border="~ main rounded-md">
<button
border="r main"
p="2"
font="mono"
outline="!none"
hover:bg="gray-400 opacity-20"
@click="counter -= 1"
>
-
</button>
<span m="auto" p="2">{{ counter }}</span>
<button
border="l main"
p="2"
font="mono"
outline="!none"
hover:bg="gray-400 opacity-20"
@click="counter += 1"
>
+
</button>
</div>
</template>
================================================
FILE: demo/starter/package.json
================================================
{
"name": "slidev-demo",
"private": true,
"scripts": {
"build": "slidev build",
"dev": "nodemon -w '../../packages/slidev/dist/*.mjs' --exec \"slidev ./slides.md --open=false --log=info --inspect\"",
"export": "slidev export",
"export-notes": "slidev export-notes"
},
"devDependencies": {
"@slidev/cli": "workspace:*",
"@slidev/theme-default": "catalog:themes",
"@slidev/theme-seriph": "catalog:themes",
"nodemon": "catalog:dev",
"vue": "catalog:frontend"
}
}
================================================
FILE: demo/starter/pages/imported-slides.md
================================================
# Imported Slides
You can split your slides.md into multiple files and organize them as you want using the `src` attribute.
#### `slides.md`
```markdown
# Page 1
Page 2 from main entry.
---
## src: ./subpage.md
```
<br>
#### `subpage.md`
```markdown
# Page 2
Page 2 from another file.
```
[Learn more](https://sli.dev/guide/syntax.html#importing-slides)
================================================
FILE: demo/starter/slides.md
================================================
---
# try also 'default' to start simple
theme: seriph
# random image from a curated Unsplash collection by Anthony
# like them? see https://unsplash.com/collections/94734566/slidev
background: https://cover.sli.dev
# some information about your slides (markdown enabled)
title: Welcome to Slidev
info: |
## Slidev Starter Template
Presentation slides for developers.
Learn more at [Sli.dev](https://sli.dev)
# apply UnoCSS classes to the current slide
class: text-center
# https://sli.dev/features/drawing
drawings:
persist: false
# slide transition: https://sli.dev/guide/animations.html#slide-transitions
transition: slide-left
# enable Comark Syntax: https://comark.dev/syntax/markdown
comark: true
# duration of the presentation
duration: 35min
---
# Welcome to Slidev
Presentation slides for developers
<div @click="$slidev.nav.next" class="mt-12 py-1" hover:bg="white op-10">
Press Space for next page <carbon:arrow-right />
</div>
<div class="abs-br m-6 text-xl">
<button @click="$slidev.nav.openInEditor()" title="Open in Editor" class="slidev-icon-btn">
<carbon:edit />
</button>
<a href="https://github.com/slidevjs/slidev" target="_blank" class="slidev-icon-btn">
<carbon:logo-github />
</a>
</div>
<!--
The last comment block of each slide will be treated as slide notes. It will be visible and editable in Presenter Mode along with the slide. [Read more in the docs](https://sli.dev/guide/syntax.html#notes)
-->
---
transition: fade-out
---
# What is Slidev?
Slidev is a slides maker and presenter designed for developers, consist of the following features
- 📝 **Text-based** - focus on the content with Markdown, and then style them later
- 🎨 **Themable** - themes can be shared and re-used as npm packages
- 🧑💻 **Developer Friendly** - code highlighting, live coding with autocompletion
- 🤹 **Interactive** - embed Vue components to enhance your expressions
- 🎥 **Recording** - built-in recording and camera view
- 📤 **Portable** - export to PDF, PPTX, PNGs, or even a hostable SPA
- 🛠 **Hackable** - virtually anything that's possible on a webpage is possible in Slidev
<br>
<br>
Read more about [Why Slidev?](https://sli.dev/guide/why)
<!--
You can have `style` tag in markdown to override the style for the current page.
Learn more: https://sli.dev/features/slide-scope-style
-->
<style>
h1 {
background-color: #2B90B6;
background-image: linear-gradient(45deg, #4EC5D4 10%, #146b8c 20%);
background-size: 100%;
-webkit-background-clip: text;
-moz-background-clip: text;
-webkit-text-fill-color: transparent;
-moz-text-fill-color: transparent;
}
</style>
<!--
Here is another comment.
-->
---
transition: slide-up
level: 2
---
# Navigation
Hover on the bottom-left corner to see the navigation's controls panel, [learn more](https://sli.dev/guide/ui#navigation-bar)
## Keyboard Shortcuts
| | |
| --------------------------------------------------- | --------------------------- |
| <kbd>right</kbd> / <kbd>space</kbd> | next animation or slide |
| <kbd>left</kbd> / <kbd>shift</kbd><kbd>space</kbd> | previous animation or slide |
| <kbd>up</kbd> | previous slide |
| <kbd>down</kbd> | next slide |
<!-- https://sli.dev/guide/animations.html#click-animation -->
<img
v-click
class="absolute -bottom-9 -left-7 w-80 opacity-50"
src="https://sli.dev/assets/arrow-bottom-left.svg"
alt=""
/>
<p v-after class="absolute bottom-23 left-45 opacity-30 transform -rotate-10">Here!</p>
---
layout: two-cols
layoutClass: gap-16
---
# Table of contents
You can use the `Toc` component to generate a table of contents for your slides:
```html
<Toc minDepth="1" maxDepth="1" />
```
The title will be inferred from your slide content, or you can override it with `title` and `level` in your frontmatter.
::right::
<Toc text-sm minDepth="1" maxDepth="2" />
---
layout: image-right
image: https://cover.sli.dev
---
# Code
Use code snippets and get the highlighting directly, and even types hover!
```ts [filename-example.ts] {all|4|6|6-7|9|all} twoslash
// TwoSlash enables TypeScript hover information
// and errors in markdown code blocks
// More at https://shiki.style/packages/twoslash
import { computed, ref } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
doubled.value = 2
```
<arrow v-click="[4, 5]" x1="350" y1="310" x2="195" y2="342" color="#953" width="2" arrowSize="1" />
<!-- This allow you to embed external code blocks -->
<<< @/snippets/external.ts#snippet
<!-- Footer -->
[Learn more](https://sli.dev/features/line-highlighting)
<!-- Inline style -->
<style>
.footnotes-sep {
@apply mt-5 opacity-10;
}
.footnotes {
@apply text-sm opacity-75;
}
.footnote-backref {
display: none;
}
</style>
<!--
Notes can also sync with clicks
[click] This will be highlighted after the first click
[click] Highlighted with `count = ref(0)`
[click:3] Last click (skip two clicks)
-->
---
level: 2
---
# Shiki Magic Move
Powered by [shiki-magic-move](https://shiki-magic-move.netlify.app/), Slidev supports animations across multiple code snippets.
Add multiple code blocks and wrap them with <code>````md magic-move</code> (four backticks) to enable the magic move. For example:
````md magic-move {lines: true}
```ts {*|2|*}
// step 1
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
```
```ts {*|1-2|3-4|3-4,8}
// step 2
export default {
data() {
return {
author: {
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
}
}
}
}
```
```ts
// step 3
export default {
data: () => ({
author: {
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
}
})
}
```
Non-code blocks are ignored.
```vue
<!-- step 4 -->
<script setup>
const author = {
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
}
</script>
```
````
---
# Components
<div grid="~ cols-2 gap-4">
<div>
You can use Vue components directly inside your slides.
We have provided a few built-in components like `<Tweet/>` and `<Youtube/>` that you can use directly. And adding your custom components is also super easy.
```html
<Counter :count="10" />
```
<!-- ./components/Counter.vue -->
<Counter :count="10" m="t-4" />
Check out [the guides](https://sli.dev/builtin/components.html) for more.
</div>
<div>
```html
<Tweet id="1390115482657726468" />
```
<Tweet id="1390115482657726468" scale="0.65" />
</div>
</div>
<!--
Presenter note with **bold**, *italic*, and ~~striked~~ text.
Also, HTML elements are valid:
<div class="flex w-full">
<span style="flex-grow: 1;">Left content</span>
<span>Right content</span>
</div>
-->
---
class: px-20
---
# Themes
Slidev comes with powerful theming support. Themes can provide styles, layouts, components, or even configurations for tools. Switching between themes by just **one edit** in your frontmatter:
<div grid="~ cols-2 gap-2" m="t-2">
```yaml
---
theme: default
---
```
```yaml
---
theme: seriph
---
```
<img border="rounded" src="https://github.com/slidevjs/themes/blob/main/screenshots/theme-default/01.png?raw=true" alt="">
<img border="rounded" src="https://github.com/slidevjs/themes/blob/main/screenshots/theme-seriph/01.png?raw=true" alt="">
</div>
Read more about [How to use a theme](https://sli.dev/guide/theme-addon#use-theme) and
check out the [Awesome Themes Gallery](https://sli.dev/resources/theme-gallery).
---
# Clicks Animations
You can add `v-click` to elements to add a click animation.
<div v-click>
This shows up when you click the slide:
```html
<div v-click>This shows up when you click the slide.</div>
```
</div>
<br>
<v-click>
The <span v-mark.red="3"><code>v-mark</code> directive</span>
also allows you to add
<span v-mark.circle.orange="4">inline marks</span>
, powered by [Rough Notation](https://roughnotation.com/):
```html
<span v-mark.underline.orange>inline markers</span>
```
</v-click>
<div mt-20 v-click>
[Learn more](https://sli.dev/guide/animations#click-animation)
</div>
---
# Motions
Motion animations are powered by [@vueuse/motion](https://motion.vueuse.org/), triggered by `v-motion` directive.
```html
<div
v-motion
:initial="{ x: -80 }"
:enter="{ x: 0 }"
:click-3="{ x: 80 }"
:leave="{ x: 1000 }"
>
Slidev
</div>
```
<div class="w-60 relative">
<div class="relative w-40 h-40">
<img
v-motion
:initial="{ x: 800, y: -100, scale: 1.5, rotate: -50 }"
:enter="final"
class="absolute inset-0"
src="https://sli.dev/logo-square.png"
alt=""
/>
<img
v-motion
:initial="{ y: 500, x: -100, scale: 2 }"
:enter="final"
class="absolute inset-0"
src="https://sli.dev/logo-circle.png"
alt=""
/>
<img
v-motion
:initial="{ x: 600, y: 400, scale: 2, rotate: 100 }"
:enter="final"
class="absolute inset-0"
src="https://sli.dev/logo-triangle.png"
alt=""
/>
</div>
<div
class="text-5xl absolute top-14 left-40 text-[#2B90B6] -z-1"
v-motion
:initial="{ x: -80, opacity: 0}"
:enter="{ x: 0, opacity: 1, transition: { delay: 2000, duration: 1000 } }">
Slidev
</div>
</div>
<!-- vue script setup scripts can be directly used in markdown, and will only affects current page -->
<script setup lang="ts">
const final = {
x: 0,
y: 0,
rotate: 0,
scale: 1,
transition: {
type: 'spring',
damping: 10,
stiffness: 20,
mass: 2
}
}
</script>
<div
v-motion
:initial="{ x:35, y: 30, opacity: 0}"
:enter="{ y: 0, opacity: 1, transition: { delay: 3500 } }">
[Learn more](https://sli.dev/guide/animations.html#motion)
</div>
---
# $\LaTeX$
$\LaTeX$ is supported out-of-box. Powered by [$\KaTeX$](https://katex.org/).
<div h-3 />
Inline $\sqrt{3x-1}+(1+x)^2$
Block
$$ {1|3|all}
\begin{aligned}
\nabla \cdot \vec{E} &= \frac{\rho}{\varepsilon_0} \\
\nabla \cdot \vec{B} &= 0 \\
\nabla \times \vec{E} &= -\frac{\partial\vec{B}}{\partial t} \\
\nabla \times \vec{B} &= \mu_0\vec{J} + \mu_0\varepsilon_0\frac{\partial\vec{E}}{\partial t}
\end{aligned}
$$
[Learn more](https://sli.dev/features/latex)
---
# Diagrams
You can create diagrams / graphs from textual descriptions, directly in your Markdown.
<div class="grid grid-cols-4 gap-5 pt-4 -mb-6">
```mermaid {scale: 0.5, alt: 'A simple sequence diagram'}
sequenceDiagram
Alice->John: Hello John, how are you?
Note over Alice,John: A typical interaction
```
```mermaid {theme: 'neutral', scale: 0.8}
graph TD
B[Text] --> C{Decision}
C -->|One| D[Result 1]
C -->|Two| E[Result 2]
```
```mermaid
mindmap
root((mindmap))
Origins
Long history
::icon(fa fa-book)
Popularisation
British popular psychology author Tony Buzan
Research
On effectiveness<br/>and features
On Automatic creation
Uses
Creative techniques
Strategic planning
Argument mapping
Tools
Pen and paper
Mermaid
```
```plantuml {scale: 0.7}
@startuml
package "Some Group" {
HTTP - [First Component]
[Another Component]
}
node "Other Groups" {
FTP - [Second Component]
[First Component] --> FTP
}
cloud {
[Example 1]
}
database "MySql" {
folder "This is my folder" {
[Folder 3]
}
frame "Foo" {
[Frame 4]
}
}
[Another Component] --> [Example 1]
[Example 1] --> [Folder 3]
[Folder 3] --> [Frame 4]
@enduml
```
</div>
Learn more: [Mermaid Diagrams](https://sli.dev/features/mermaid) and [PlantUML Diagrams](https://sli.dev/features/plantuml)
---
foo: bar
dragPos:
square: 691,32,167,_,-16
---
# Draggable Elements
Double-click on the draggable elements to edit their positions.
<br>
###### Directive Usage
```md
<img v-drag="'square'" src="https://sli.dev/logo.png">
```
<br>
###### Component Usage
```md
<v-drag text-3xl>
<div class="i-carbon:arrow-up" />
Use the `v-drag` component to have a draggable container!
</v-drag>
```
<v-drag pos="663,206,261,_,-15">
<div text-center text-3xl border border-main rounded>
Double-click me!
</div>
</v-drag>
<img v-drag="'square'" src="https://sli.dev/logo.png">
###### Draggable Arrow
```md
<v-drag-arrow two-way />
```
<v-drag-arrow pos="67,452,253,46" two-way op70 />
---
src: ./pages/imported-slides.md
hide: false
---
---
# Monaco Editor
Slidev provides built-in Monaco Editor support.
Add `{monaco}` to the code block to turn it into an editor:
```ts {monaco}
import { ref } from 'vue'
import { emptyArray } from './external'
const arr = ref(emptyArray(10))
```
Use `{monaco-run}` to create an editor that can execute the code directly in the slide:
```ts {monaco-run}
import { version } from 'vue'
import { emptyArray, sayHello } from './external'
sayHello()
console.log(`vue ${version}`)
console.log(emptyArray<number>(10).reduce(fib => [...fib, fib.at(-1)! + fib.at(-2)!], [1, 1]))
```
---
layout: center
class: text-center
---
# Learn More
[Documentation](https://sli.dev) · [GitHub](https://github.com/slidevjs/slidev) · [Showcases](https://sli.dev/resources/showcases)
<PoweredBySlidev mt-10 />
================================================
FILE: demo/starter/snippets/external.ts
================================================
/* eslint-disable no-console */
// #region snippet
// Inside ./snippets/external.ts
export function emptyArray<T>(length: number) {
return Array.from<T>({ length })
}
// #endregion snippet
export function sayHello() {
console.log('Hello from snippets/external.ts')
}
================================================
FILE: demo/starter/style.css
================================================
/* add any global style here */
================================================
FILE: demo/starter/vite.config.ts
================================================
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [],
})
================================================
FILE: demo/vue-runner/package.json
================================================
{
"private": true,
"scripts": {
"build": "slidev build",
"dev": "nodemon -w '../../packages/slidev/dist/*.mjs' --exec \"slidev ./slides.md --open=false --log=info --inspect\"",
"export": "slidev export",
"export-notes": "slidev export-notes"
},
"devDependencies": {
"@slidev/cli": "workspace:*",
"@slidev/theme-default": "catalog:themes",
"@slidev/theme-seriph": "catalog:themes",
"@vue/compiler-sfc": "catalog:demo",
"nodemon": "catalog:dev",
"vue": "catalog:frontend"
}
}
================================================
FILE: demo/vue-runner/setup/code-runners.ts
================================================
/* eslint-disable no-new-func */
import { defineCodeRunnersSetup } from '@slidev/types'
export default defineCodeRunnersSetup(() => {
return {
// Support Vue SFC
async vue(code) {
const Vue = await import('vue')
const { parse, compileScript } = await import('@vue/compiler-sfc')
// Compile the script, note this demo does not handle Vue styles
const sfc = parse(code)
let scripts = compileScript(sfc.descriptor, {
id: sfc.descriptor.filename,
genDefaultAs: '__Component',
inlineTemplate: true,
}).content
// Replace Vue imports to object destructuring
// Only for simple demo, it doesn't work with imports from other packages
scripts = scripts.replace(/import (\{[^}]+\}) from ['"]vue['"]/g, (_, imports) => `const ${imports.replace(/\sas\s/g, ':')} = Vue`)
scripts += '\nreturn __Component'
// Create function to evaluate the script and get the component
// Note this is not sandboxed, it's NOT secure.
const component = new Function(`return (Vue) => {${scripts}}`)()(Vue)
// Mount the component
const app = Vue.createApp(component)
const el = document.createElement('div')
app.mount(el)
return {
element: el,
}
},
}
})
================================================
FILE: demo/vue-runner/setup/shiki.ts
================================================
import type { ShikiSetupReturn } from '@slidev/types'
import { defineShikiSetup } from '@slidev/types'
export default defineShikiSetup((): ShikiSetupReturn => {
return {
langs: [
'ts',
'js',
'vue',
'html',
],
}
})
================================================
FILE: demo/vue-runner/slides.md
================================================
---
layout: default
---
# Simple Vue SFC Runner
<!-- eslint-skip -->
```vue {monaco-run}
<script setup>
import { computed, ref } from 'vue'
const counter = ref(1)
const doubled = computed(() => counter.value * 2)
function inc() { counter.value++ }
</script>
<template>
<div class="select-none text-lg flex gap-4 items-center">
<span class="text-gray text-lg">
<span class="text-orange">{{ counter }}</span>
* 2 =
<span class="text-green">{{ doubled }}</span>
</span>
<button class="border border-main p2 rounded" @click="inc">+1</button>
<button class="border border-main p2 rounded" @click="counter -= 1">-1</button>
</div>
</template>
```
---
This is a demo to prove the extensibility of Slidev Code Runners.
Refer to `./setup/monaco-runner.ts` for the implementation.
Note that there is a lot of edge cases that this demo is not handling. Extra work is needed to make it production ready.
================================================
FILE: docs/.gitignore
================================================
node_modules
dist
.vitepress/@slidev
.vitepress/cache
================================================
FILE: docs/.vitepress/addons.ts
================================================
import type { ThemeInfo } from './themes'
export type AddonInfo = Omit<ThemeInfo, 'previews'>
export const official: AddonInfo[] = [
{
id: '',
link: '#',
name: 'Work in Progress',
description: '',
tags: [],
author: {
name: '',
},
},
]
export const community: AddonInfo[] = [
{
id: 'slidev-addon-python-runner',
name: 'Python Runner',
description: 'Run actual Python code in your slides',
tags: ['Code runner'],
author: {
name: '_Kerman',
link: 'https://github.com/KermanX',
},
repo: 'https://github.com/KermanX/slidev-addon-python-runner',
},
{
id: 'slidev-addon-tldraw',
name: 'tldraw',
description: 'Embed tldraw diagrams directly in Slidev, with in-slide editing support',
tags: ['Integration', 'Diagram'],
author: {
name: 'Albert Brand',
link: 'https://github.com/AlbertBrand',
},
repo: 'https://github.com/AlbertBrand/slidev-addon-tldraw',
},
{
id: 'slidev-addon-react',
name: 'React Components',
description: 'Use React components with JSX/TSX in your Slidev presentations.',
tags: ['React', 'Component', 'Integration'],
repo: 'https://github.com/Ygilany/slidev-addon-react',
author: {
name: 'YGilany',
link: 'https://github.com/YGilany',
},
},
{
id: 'slidev-addon-typst',
name: 'Typst',
description: 'Typst addon for Slidev',
tags: ['Integration', 'Diagram'],
author: {
name: 'Shigma',
link: 'https://github.com/shigma',
},
repo: 'https://github.com/shigma/slidev-addon-typst',
},
{
id: 'slidev-addon-fancy-arrow',
name: 'Fancy Arrow',
description: 'Hand drawn arrows with various styling and positioning options',
tags: ['Component'],
author: {
name: 'whitphx',
link: 'https://github.com/whitphx',
},
repo: 'https://github.com/whitphx/slidev-addon-fancy-arrow',
},
{
id: 'slidev-addon-sync',
name: 'Remote Sync',
description: 'Sync component for Slidev static build that uses a SSE or WS server',
tags: ['Remote control', 'Navigation'],
author: {
name: 'Tony Cabaye',
link: 'https://github.com/tonai',
},
repo: 'https://github.com/Smile-SA/slidev-addon-sync',
},
{
id: 'slidev-addon-tikzjax',
name: 'tikzjax',
description: 'Compile TikZ/Chemfig/... to SVG and display them in Slidev',
tags: ['Integration', 'Diagram'],
author: {
name: 'Ethan Goh',
link: 'https://github.com/7086cmd',
},
repo: 'https://github.com/7086cmd/slidev-addon-tikzjax',
},
{
id: 'slidev-component-pager',
name: 'Pager',
description: 'Show current page and total page number',
tags: ['Component', 'Navigation'],
author: {
name: 'Tony Cabaye',
link: 'https://github.com/tonai',
},
repo: 'https://github.com/Smile-SA/slidev-component-pager',
},
{
id: 'slidev-component-poll',
name: 'Poll and Quiz',
description: 'Poll and Quiz components for Slidev',
tags: ['Component'],
author: {
name: 'Tony Cabaye',
link: 'https://github.com/tonai',
},
repo: 'https://github.com/Smile-SA/slidev-component-poll',
},
{
id: 'slidev-component-progress',
name: 'Progress',
description: 'Show interactive progress bar for Slidev',
tags: ['Tool', 'Navigation'],
author: {
name: 'Tony Cabaye',
link: 'https://github.com/tonai',
},
repo: 'https://github.com/Smile-SA/slidev-component-progress',
},
{
id: 'slidev-component-scroll',
name: 'Mouse Scroll',
description: 'Use mouse wheel for navigating',
tags: ['Navigation'],
author: {
name: 'Tony Cabaye',
link: 'https://github.com/tonai',
},
repo: 'https://github.com/Smile-SA/slidev-component-scroll',
},
{
id: 'slidev-component-spotlight',
name: 'Spotlight',
description: 'Activate a spotlight to highlight a specific region by holding a key',
tags: ['Tool'],
author: {
name: 'Tony Cabaye',
link: 'https://github.com/tonai',
},
repo: 'https://github.com/Smile-SA/slidev-component-spotlight',
},
{
id: 'slidev-component-zoom',
name: 'Zooming',
description: 'Allow zooming inside the slides',
tags: ['Tool'],
author: {
name: 'Tony Cabaye',
link: 'https://github.com/tonai',
},
repo: 'https://github.com/Smile-SA/slidev-component-zoom',
},
{
id: 'slidev-addon-rabbit',
name: 'Rabbit',
description: 'Presentation time management for Slidev inspired by Rabbit',
tags: ['Tool', 'Navigation'],
author: {
name: 'kaakaa',
link: 'https://github.com/kaakaa',
},
repo: 'https://github.com/kaakaa/slidev-addon-rabbit',
},
{
id: 'slidev-addon-stem',
name: 'STEM',
description: 'Slidev addon for scientific presentation',
tags: ['Component', 'Layout'],
author: {
name: 'yutaka-shoji',
link: 'https://github.com/yutaka-shoji',
},
repo: 'https://github.com/yutaka-shoji/slidev-addon-stem',
},
{
id: 'slidev-addon-naive',
name: 'Naive UI',
description: 'Brings Naive UI components into Slidev',
tags: ['Component'],
author: {
name: 'Samuel Huang',
link: 'https://sghuang.com',
},
repo: 'https://github.com/sghuang19/slidev-addon-naive',
},
{
id: 'slidev-addon-hls-player',
name: 'HLS player',
description: 'Add a basic hls.js powered video player on your slides to show HTTP Live Streaming videos',
tags: ['hls', 'video'],
author: {
name: 'Albert Brand',
link: 'https://github.com/AlbertBrand',
},
repo: 'https://github.com/AlbertBrand/slidev-addon-hls-player',
},
{
id: 'slidev-addon-window-mockup',
name: 'Window Mockup',
description: 'Styled window frames',
tags: ['Component'],
author: {
name: 'whitphx',
link: 'https://github.com/whitphx',
},
repo: 'https://github.com/whitphx/slidev-addon-window-mockup',
},
{
id: 'slidev-addon-bpmn',
name: 'BPMN viewer',
description: 'Visualize bpmn-files in your slidev',
tags: ['Component'],
author: {
name: 'emaarco',
link: 'https://github.com/emaarco',
},
repo: 'https://github.com/emaarco/slidev-addon-bpmn',
},
{
id: 'slidev-addon-p5',
name: 'Runner for p5js',
description: 'Display, edit and run p5js sketches in your slidev',
tags: ['Component', 'Code runner'],
author: {
name: 'mjvo',
link: 'https://github.com/mjvo',
},
repo: 'https://github.com/mjvo/slidev-addon-p5',
},
// Add yours here!
{
id: '',
link: 'https://github.com/slidevjs/slidev/edit/main/docs/.vitepress/addons.ts',
name: 'Yours?',
description: 'Click here to submit your addon :)',
tags: [],
author: {
name: '',
},
},
]
================================================
FILE: docs/.vitepress/config.ts
================================================
import type { DefaultTheme } from 'vitepress'
import { fileURLToPath } from 'node:url'
import { transformerTwoslash } from '@shikijs/vitepress-twoslash'
import { defineConfig } from 'vitepress'
import { groupIconMdPlugin } from 'vitepress-plugin-group-icons'
import { version } from '../package.json'
import Customizations from './customizations'
import { Advanced, BuiltIn, Guides, Resources } from './pages'
import { getSidebarObject } from './sidebar-gen'
export const slidebars: DefaultTheme.SidebarItem[] = [
{
text: 'Guide',
items: Guides,
},
{
text: 'Advanced',
items: Advanced,
},
{
text: 'Customizations',
items: Customizations,
},
{
text: 'Built-in',
items: BuiltIn,
},
{
text: 'Resources',
items: Resources,
},
]
export default defineConfig({
title: 'Slidev',
description: 'Presentation slides for developers',
head: [
['link', { rel: 'icon', type: 'image/png', href: '/favicon.png' }],
['meta', { name: 'author', content: 'Anthony Fu' }],
['meta', { property: 'og:title', content: 'Slidev' }],
['meta', { property: 'og:image', content: 'https://sli.dev/og-image.png' }],
['meta', { property: 'og:description', content: 'Presentation slides for developers' }],
['meta', { name: 'twitter:card', content: 'summary_large_image' }],
['meta', { name: 'twitter:creator', content: '@slidevjs' }],
['meta', { name: 'twitter:image', content: 'https://sli.dev/og-image.png' }],
['link', { rel: 'dns-prefetch', href: 'https://fonts.gstatic.com' }],
['link', { rel: 'preconnect', crossorigin: 'anonymous', href: 'https://fonts.gstatic.com' }],
['link', { href: 'https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@200;400;500&family=Inter:wght@200;400;500;600', rel: 'stylesheet' }],
],
markdown: {
theme: {
light: 'vitesse-light',
dark: 'vitesse-dark',
},
async shikiSetup(shiki) {
await shiki.loadLanguage(
'html',
'xml',
'vue',
'markdown',
'mermaid',
'latex',
)
},
codeTransformers: [
transformerTwoslash({
twoslashOptions: {
// The @slidev/* installed in docs package are very old and should only be used in the homepage demo
vfsRoot: fileURLToPath(import.meta.url),
compilerOptions: {
resolveJsonModule: true,
moduleResolution: /* Bundler */ 100,
},
},
}),
],
config(md) {
md.use(groupIconMdPlugin)
},
},
cleanUrls: true,
themeConfig: {
logo: '/logo.svg',
editLink: {
pattern: 'https://github.com/slidevjs/slidev/edit/main/docs/:path',
text: 'Suggest changes to this page',
},
search: {
provider: 'local',
},
nav: [
{
text: '📖 Guide',
items: [
...Guides,
{
text: 'Advanced',
items: Advanced,
},
],
},
{
text: '✨ Features',
link: '/features/',
},
{
text: 'Reference',
items: [
{
text: 'Built-in',
items: BuiltIn,
},
{
text: 'Customize',
items: Customizations,
},
],
},
{
text: 'Resources',
items: Resources,
},
],
socialLinks: [
{ icon: 'github', link: 'https://github.com/slidevjs/slidev' },
{ icon: 'twitter', link: 'https://twitter.com/slidevjs' },
{ icon: 'discord', link: 'https://chat.sli.dev' },
],
sidebar: {
'/guide/': slidebars,
'/themes/': slidebars,
'/addons/': slidebars,
'/custom/': slidebars,
'/builtin/': slidebars,
'/resources/': slidebars,
// eslint-disable-next-line antfu/no-top-level-await
...await getSidebarObject(),
'/features/': [],
'/': slidebars,
},
footer: {
message: 'Released under the MIT License.',
copyright: 'Copyright © 2020-2025 Anthony Fu.',
},
},
locales: {
root: {
label: `English (v${version})`,
},
zh: {
label: '简体中文',
link: 'https://cn.sli.dev/',
},
ja: {
label: '日本語',
link: 'https://ja.sli.dev/',
},
},
})
================================================
FILE: docs/.vitepress/customizations.ts
================================================
export default [
{
text: 'Configurations',
link: '/custom/',
},
{
text: 'Directory Structure',
link: '/custom/directory-structure',
},
{
text: 'Configure Highlighter',
link: '/custom/config-highlighter',
},
{
text: 'Configure Vite and Plugins',
link: '/custom/config-vite',
},
{
text: 'Configure Vue App',
link: '/custom/config-vue',
},
{
text: 'Configure UnoCSS',
link: '/custom/config-unocss',
},
{
text: 'Configure Code Runners',
link: '/custom/config-code-runners',
},
{
text: 'Configure Transformers',
link: '/custom/config-transformers',
},
{
text: 'Configure Monaco',
link: '/custom/config-monaco',
},
{
text: 'Configure KaTeX',
link: '/custom/config-katex',
},
{
text: 'Configure Mermaid',
link: '/custom/config-mermaid',
},
{
text: 'Configure Mermaid Renderer',
link: '/custom/config-mermaid-renderer',
},
{
text: 'Configure Routes',
link: '/custom/config-routes',
},
{
text: 'Configure Shortcuts',
link: '/custom/config-shortcuts',
},
{
text: 'Configure Context Menu',
link: '/custom/config-context-menu',
},
{
text: 'Configure Fonts',
link: '/custom/config-fonts',
},
{
text: 'Configure Pre-Parser',
link: '/custom/config-parser',
},
]
================================================
FILE: docs/.vitepress/pages.ts
================================================
export const Guides = [
{
text: 'Why Slidev',
link: '/guide/why',
},
{
text: 'Getting Started',
link: '/guide/',
},
{
text: 'Syntax Guide',
link: '/guide/syntax',
},
{
text: 'User Interface',
link: '/guide/ui',
},
{
text: 'Animations',
link: '/guide/animations',
},
{
text: 'Theme & Addons',
link: '/guide/theme-addon',
},
{
text: 'Components',
link: '/guide/component',
},
{
text: 'Layouts',
link: '/guide/layout',
},
{
text: 'Exporting',
link: '/guide/exporting',
},
{
text: 'Hosting',
link: '/guide/hosting',
},
{
text: 'Work with AI',
link: '/guide/work-with-ai',
},
{
text: 'FAQ',
link: '/guide/faq',
},
]
export const BuiltIn = [
{
text: 'CLI',
link: '/builtin/cli',
},
{
text: 'Components',
link: '/builtin/components',
},
{
text: 'Layouts',
link: '/builtin/layouts',
},
]
export const Advanced = [
{
text: 'Global Context',
link: '/guide/global-context',
},
{
text: 'Writing Layouts',
link: '/guide/write-layout',
},
{
text: 'Writing Themes',
link: '/guide/write-theme',
},
{
text: 'Writing Addons',
link: '/guide/write-addon',
},
]
export const Resources = [
{
text: 'Showcases',
link: '/resources/showcases',
},
{
text: 'Theme Gallery',
link: '/resources/theme-gallery',
},
{
text: 'Addon Gallery',
link: '/resources/addon-gallery',
},
{
text: 'Learning Resources',
link: '/resources/learning',
},
{
text: 'Curated Covers',
link: '/resources/covers',
},
{
text: 'Release Notes',
link: 'https://github.com/slidevjs/slidev/releases',
},
]
================================================
FILE: docs/.vitepress/showcases.ts
================================================
export interface ShowCaseInfo {
title: string
cover: string
slidesLink?: string
sourceLink?: string
videoLink?: string
at?: string
datetime: string
author: {
name: string
link?: string
}
}
export const showcases: ShowCaseInfo[] = [
{
title: 'Composable Vue',
cover: `${import.meta.env.BASE_URL}showcases/composable-vue.png`,
author: {
name: 'Anthony Fu',
link: 'https://github.com/antfu',
},
slidesLink: 'https://talks.antfu.me/2021/composable-vue/',
sourceLink: 'https://github.com/antfu/talks/tree/main/2021-04-29',
at: 'VueDay 2021',
datetime: '2021-04-29',
},
{
title: 'Developer Seonglae',
cover: 'https://seonglae-slides.vercel.app/og.png',
author: {
name: 'Seonglae Cho',
link: 'https://github.com/seonglae',
},
slidesLink: 'https://seonglae-slides.vercel.app',
sourceLink: 'https://github.com/seonglae/seonglae-slides',
at: 'Seongland',
datetime: '2021-05-10',
},
{
title: 'Vue 3 > Vue 2 + 1',
cover: 'https://user-images.githubusercontent.com/11247099/122246420-1df97b80-cef9-11eb-9c57-7751c6999deb.png',
author: {
name: 'Thorsten Lünborg',
link: 'https://github.com/LinusBorg',
},
slidesLink: 'http://vueday-2021.linusb.org',
sourceLink: 'https://github.com/LinusBorg/vueday-enterjs-vue3',
at: 'Enter.js Vue Day',
datetime: '2021-06-15',
},
// {
// title: 'Simply Publish Your Package to npm',
// author: {
// name: 'Lucky Dewa Satria',
// link: 'https://github.com/lucky401',
// },
// at: 'Weekly sharing',
// slidesLink: 'https://masukin.link/talks/simply-publish-your-package-to-npm',
// cover: 'https://masukin.link/talks-cover-npm.png',
// datetime: '2021-06-12',
// },
// {
// title: 'Create Icon Package With Vue and Rollup',
// author: {
// name: 'Lucky Dewa Satria',
// link: 'https://github.com/lucky401',
// },
// at: 'Weekly Sharing',
// slidesLink: 'https://masukin.link/talks/create-icon-package-with-vue-and-rollup',
// sourceLink: 'https://github.com/lucky401/Create-Icon-Package-With-Vue-and-Rollup',
// cover: 'https://masukin.link/talks-cover-create-icon-package-with-vue-and-rollup.png',
// datetime: '2021-06-19',
// },
{
title: 'BeAPT',
author: {
name: 'Daniel Sousa @TutoDS',
link: 'https://github.com/tutods',
},
at: 'Presentation of my college final project',
slidesLink: 'https://beapt-presentation.netlify.app',
sourceLink: 'https://github.com/TutoDS/lei-project/tree/master/presentation',
cover: 'https://raw.githubusercontent.com/TutoDS/lei-project/master/presentation/cover.png',
datetime: '2021-07-20',
},
{
title: 'Prisma as my ORM for PostgreSQL',
cover: 'https://raw.githubusercontent.com/cedric25/prisma-talk/main/cover-for-slidev.png',
author: {
name: 'Cedric Nicoloso',
link: 'https://github.com/cedric25',
},
slidesLink: 'https://prisma-talk.netlify.app/',
sourceLink: 'https://github.com/cedric25/prisma-talk',
at: 'LyonJS Meetup',
datetime: '2021-07-21',
},
{
title: 'Introduction to SVG',
cover: 'https://raw.githubusercontent.com/lyqht/intro-to-svg-slides/main/intro-to-svg-slides-cover.png',
author: {
name: 'Estee Tey',
link: 'https://github.com/lyqht',
},
slidesLink: 'https://lyqht.github.io/intro-to-svg-slides/',
sourceLink: 'https://github.com/lyqht/intro-to-svg-slides',
at: 'Thoughtworks Internal Lunch & Learn',
datetime: '2021-11-12',
},
{
title: 'Git\'s Most Wanted',
cover: 'https://cdn.jsdelivr.net/gh/alexanderdavide/git-most-wanted@assets/slides-export/01.png',
author: {
name: 'Alexander Eble',
link: 'https://github.com/alexanderdavide',
},
slidesLink: 'https://alexeble.de/talks/git-most-wanted/',
sourceLink: 'https://github.com/alexanderdavide/git-most-wanted',
at: 'Internal Tech Talk',
datetime: '2022-03-11',
},
{
title: 'OpenFunction 202',
cover: 'https://s2.loli.net/2022/05/22/4zsCnkQRFoAU1E5.png',
author: {
name: 'Haili Zhang',
link: 'https://github.com/webup',
},
slidesLink: 'https://openfunction-talks.netlify.app/2022/202-node-async/',
sourceLink: 'https://github.com/webup/openfunction-talks/tree/main/202-node-async',
at: 'OpenFunction Tutorial Sharing',
datetime: '2022-05-08',
},
{
title: 'Is it Okay to Pursue Functional Programming on Frontend?',
author: {
name: 'Minsu Kim , Changhui Lee',
},
at: '2022 JSConf Korea',
slidesLink: 'https://moonlit-nougat-422445.netlify.app/1',
sourceLink: 'https://github.com/alstn2468/2022-jsconf-presentation',
cover: 'https://raw.githubusercontent.com/alstn2468/2022-jsconf-presentation/main/public/images/og.png',
datetime: '2022-09-16',
},
{
title: 'Blazing slidev ppt template with naive-ui',
author: {
name: 'godkun',
},
at: 'personal sharing',
slidesLink: 'https://ppt.godkun.top',
sourceLink: 'https://github.com/godkun/ppt-template',
cover: 'https://github.com/godkun/ppt-template/raw/main/public/show.gif',
datetime: '2022-10-24',
},
{
title: 'Building a Polite Popup with Nuxt 3',
author: {
name: 'Michael Hoffmann',
link: 'https://github.com/mokkapps',
},
at: 'Vue.js Nation 2023',
slidesLink: 'https://vuejsnation-2023-talk-polite-popup.netlify.app',
sourceLink: 'https://github.com/Mokkapps/vuejsnation-2023-lightning-talk-polite-popup-nuxt-3-slides',
cover: 'https://raw.githubusercontent.com/Mokkapps/vuejsnation-2023-lightning-talk-polite-popup-nuxt-3-slides/main/screenshots/001.png',
datetime: '2023-01-25',
},
{
title: 'Dev Environment as Code',
cover: 'https://cdn.jsdelivr.net/gh/alexanderdavide/dev-environment-as-code@assets/slides-export/001.png',
author: {
name: 'Alexander Eble',
link: 'https://github.com/alexanderdavide',
},
slidesLink: 'https://alexeble.de/talks/dev-environment-as-code/',
sourceLink: 'https://github.com/alexanderdavide/dev-environment-as-code',
at: 'Internal Tech Talk',
datetime: '2022-12-01',
},
{
title: 'Exploring Social Engineering',
cover: 'https://raw.githubusercontent.com/zyf722/exploring-social-engineering-slides/main/assets/Screenshot_Cover.png',
author: {
name: 'zyf722',
link: 'https://github.com/zyf722',
},
slidesLink: 'https://zyf722.github.io/exploring-social-engineering-slides/',
sourceLink: 'https://github.com/zyf722/exploring-social-engineering-slides',
at: 'Presentation on Social Engineering in Computers in Society class',
datetime: '2023-10-20',
},
{
title: 'Diablo Health Orb Shader',
author: {
name: 'SuneBear',
link: 'https://github.com/sunebear',
},
at: 'rctAI Sessions',
slidesLink: 'https://rct-ai.github.io/frontend-slides/diablo-health-orb-shader/',
sourceLink: 'https://github.com/rct-ai/frontend-slides',
cover: 'https://github-production-user-asset-6210df.s3.amazonaws.com/7693264/284304324-db973b4c-a043-4644-932c-826169a1b4d8.gif',
datetime: '2022-09-01',
},
{
title: 'Comparison of Packaging Tools in 2023',
author: {
name: 'Peacock (Yoichi Takai)',
link: 'https://p3ac0ck.net',
},
at: 'PyCon APAC 2023',
slidesLink: 'https://slides.p3ac0ck.net/pyconapac2023/1',
sourceLink: 'https://github.com/peacock0803sz/slidev-slides/blob/7d41aa5e89ad8627cb68ae2cdbfe1681017b0408/talks/pyconapac2023/pyconapac2023.md',
cover: 'https://slides.p3ac0ck.net/pyconapac2023/cover.png',
datetime: '2023-10-28',
},
{
title: 'How Rust error handling ease web development',
author: {
name: 'Nguyễn Hồng Quân',
link: 'https://quan.hoabinh.vn',
},
at: 'FOSSASIA Summit 2024',
slidesLink: 'https://talk.quan.hoabinh.vn/rust-error-handling-ease-web-dev/',
sourceLink: 'https://hongquan@bitbucket.org/hongquan/rust-error-handling-ease-web-dev',
cover: 'https://i.imgur.com/2eBJofY.png',
datetime: '2024-04-10',
},
{
title: 'Sit Back and Relax with Fault Awareness and Robust Instant Recovery for Large Scale AI Workloads',
author: {
name: 'Neko',
link: 'https://github.com/nekomeowww',
},
at: 'KubeCon 2024 China',
slidesLink: 'https://talks.ayaka.io/nekoayaka/2024-08-21-kubecon-hk/',
sourceLink: 'https://github.com/nekomeowww/talks/tree/main/packages/2024-08-21-kubecon-hk',
cover: 'https://raw.githubusercontent.com/BaizeAI/talks/main/packages/2024-08-21-kubecon-hk/public/screenshot.png',
datetime: '2024-08-21',
},
{
title: 'Hacker Numerology',
author: {
name: 'HD Moore',
link: 'https://hdm.io',
},
at: 'LASCON 2024',
slidesLink: 'https://hdm.io/decks/2024-LASCON-Numerology/',
sourceLink: 'https://github.com/hdm/decks-2024-lascon-numerology.git',
cover: 'https://raw.githubusercontent.com/hdm/decks-2024-lascon-numerology/refs/heads/main/screenshot.png',
datetime: '2024-10-25',
},
{
title: 'Python Zero To Hero - Episode 1',
author: {
name: 'Kareim Tarek',
link: 'https://kareimgazer.github.io/',
},
at: 'Kareem Kreates YouTube Channel',
slidesLink: 'https://kareimgazer.github.io/py-intro/',
sourceLink: 'https://github.com/KareimGazer/py-intro',
cover: 'https://i.ytimg.com/vi/hVMaPBrWvAo/hqdefault.jpg',
datetime: '2025-01-12',
},
{
title: 'Taming Dependency Chaos for LLM in K8s',
author: {
name: 'Neko',
link: 'https://github.com/nekomeowww',
},
at: 'KubeCon 2025 China',
slidesLink: 'https://baizeai.github.io/talks/2025-06-11-kubecon-hk/',
sourceLink: 'https://github.com/BaizeAI/talks/tree/main/packages/2025-06-11-kubecon-hk',
cover: 'https://raw.githubusercontent.com/BaizeAI/talks/main/packages/2025-06-11-kubecon-hk/public/screenshot.png',
datetime: '2025-06-11',
},
{
title: 'Single Image Super-Resolution Based on Capsule Neural Networks',
author: {
name: 'George Corrêa de Araújo',
link: 'https://george-gca.github.io/',
},
at: 'Brazilian Conference on Intelligent Systems 2023',
slidesLink: 'https://george-gca.github.io/bracis_2023_srcaps/',
sourceLink: 'https://github.com/george-gca/bracis_2023_srcaps',
cover: 'https://raw.githubusercontent.com/george-gca/bracis_2023_srcaps/refs/heads/main/cover.png',
datetime: '2023-09-27',
},
{
title: 'Threat Modeling',
author: {
name: 'guisso',
link: 'https://github.com/fguisso',
},
at: 'OWASP Meetup',
slidesLink: 'https://guisso.dev/talks/threat-modeling',
sourceLink: 'https://github.com/fguisso/talks/tree/main/slides/threat-modeling',
cover: 'https://guisso.dev/posts/threat-modeling-intro/featured-threat-modeling_hu12396ec5bf9ecba1dda33f1443a5eb10_76776_600x0_resize_box_3.png',
datetime: '2023-09-22',
},
{
title: 'A 14-year journey developing nCine, an open-source 2D game framework',
author: {
name: 'Angelo Theodorou',
link: 'https://encelo.github.io',
},
at: '/dev/games/2025',
slidesLink: 'https://encelo.github.io/nCine_14Years_Presentation/',
sourceLink: 'https://github.com/encelo/nCine_14Years_Presentation',
cover: 'https://i.imgur.com/AbTdfhg.png',
datetime: '2025-06-05',
},
{
title: 'Reverse Engineering Denuvo in Hogwarts Legacy',
author: {
name: 'Maurice Heumann',
link: 'https://momo5502.com',
},
at: 'Navaja Negra 2025',
slidesLink: 'https://momo5502.com/slides/denuvo',
sourceLink: 'https://github.com/momo5502/denuvo-slides',
cover: 'https://raw.githubusercontent.com/momo5502/denuvo-slides/refs/heads/master/images/preview.png',
datetime: '2025-10-03',
},
// Add yours here!
{
title: 'Yours?',
author: {
name: '',
},
at: 'Submit your talk/presentation to be list here!',
slidesLink: 'https://github.com/slidevjs/slidev/edit/main/docs/.vitepress/showcases.ts',
cover: `${import.meta.env.BASE_URL}theme-placeholder.png`,
datetime: '2020-1-1',
},
].sort((a, b) => new Date(b.datetime).getTime() - new Date(a.datetime).getTime())
================================================
FILE: docs/.vitepress/sidebar-gen.ts
================================================
import type { DefaultTheme } from 'vitepress'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import fg from 'fast-glob'
import graymatter from 'gray-matter'
const root = fileURLToPath(new URL('../../', import.meta.url))
interface ParsedFile {
filepath: string
path: string
matter: graymatter.GrayMatterFile<string>
title: string
}
function parseFile(file: string) {
const filepath = join(root, file)
const path = file.replace('docs/', '').replace('.md', '')
const matter = graymatter.read(filepath)
const title = matter.data.title || matter.content.match(/^#\s+(.*)/m)?.[1] || path
return {
filepath,
path,
matter,
title,
}
}
export async function getSidebarObject() {
const map: Record<string, DefaultTheme.SidebarItem[]> = {}
const parsedFeatures: ParsedFile[] = await fg([
'docs/features/*.md',
], {
onlyFiles: true,
cwd: root,
})
.then(files => files.map(parseFile))
const parsedGuides: ParsedFile[] = await fg([
'docs/guide/*.md',
], {
onlyFiles: true,
cwd: root,
})
.then(files => files.map(parseFile))
const parsedCustoms: ParsedFile[] = await fg([
'docs/custom/*.md',
], {
onlyFiles: true,
cwd: root,
})
.then(files => files.map(parseFile))
parsedFeatures.forEach(({ matter, path }) => {
const items: DefaultTheme.SidebarItem[] = [
{
text: 'Back to',
items: [
{
text: 'All Features',
link: '/features',
},
],
},
]
function findParsed(related: string) {
related = related.replace(/#.*$/, '')
const feature = parsedFeatures.find(file => file.path === related)
if (feature) {
return {
type: 'features',
item: feature,
}
}
const guide = parsedGuides.find(file => file.path === related)
if (guide) {
return {
type: 'guide',
item: guide,
}
}
const custom = parsedCustoms.find(file => file.path === related)
if (custom) {
return {
type: 'custom',
item: custom,
}
}
return undefined
}
function frontmatterToSidebarItem(path: string | Record<string, string>): DefaultTheme.SidebarItem[] {
if (typeof path === 'string') {
const match = findParsed(path)
if (match?.type === 'features') {
return [{
text: `✨ ${match.item.title}`,
link: `/${path}`,
}]
}
if (match?.type === 'guide') {
return [{
text: `📖 ${match.item.title}`,
link: `/${path}`,
}]
}
if (match?.type === 'custom') {
return [{
text: `🛠️ ${match.item.title}`,
link: `/${path}`,
}]
}
console.warn(`Dependent file not found: ${path}`)
return [{
text: path,
link: `/${path}`,
}]
}
else {
return Object.entries(path).map(([text, link]) => ({
text,
link,
}))
}
}
if (matter.data.depends) {
items.push({
text: 'Depends on',
items: matter.data.depends.flatMap(frontmatterToSidebarItem),
})
}
if (matter.data.relates) {
items.push({
text: 'Related to',
items: matter.data.relates.flatMap(frontmatterToSidebarItem),
})
}
const derives = matter.data.derives
?? parsedFeatures.filter(f => f.matter.data.depends?.includes(path)).map(f => f.path)
if (derives.length) {
items.push({
text: 'Derives',
items: derives.flatMap(frontmatterToSidebarItem),
})
}
map[`/${path}`] = items
})
return map
}
================================================
FILE: docs/.vitepress/theme/components/AddonGallery.vue
================================================
<script setup lang="ts">
import { computed } from 'vue'
import { community, official } from '../../addons'
const props = defineProps({
collection: {
default: 'official',
},
})
const addons = computed(() => props.collection === 'official' ? official : community)
</script>
<template>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
<AddonInfo v-for="addon of addons" :key="addon.id" :addon="addon" />
</div>
</template>
================================================
FILE: docs/.vitepress/theme/components/AddonInfo.vue
================================================
<script setup lang="ts">
import type { AddonInfo } from '../../addons'
defineProps<{
addon: AddonInfo
}>()
</script>
<template>
<a :href="addon.link || addon.repo" class="block flex flex-col !decoration-none !text-unset !cursor-unset !hover:bg-gray-400/10 p-2 rounded-lg transition-all">
<div class="flex flex-wrap">
<a :href="addon.link || addon.repo" class="font-bold text-lg !text-$vp-c-text-1 !decoration-none">
{{ addon.name }}
</a>
<div class="flex-grow" />
<div class="mb-1 select-none op-80">
<span v-for="tag in addon.tags" :key="tag" class="text-xs mx-.5 px-1.5 py-.5 rounded-lg bg-gray-400/40">
{{ tag }}
</span>
</div>
</div>
<div
class="flex-grow text-current text-xs opacity-90"
:title="addon.description"
>
{{ addon.description }}
</div>
<div class="mt-2 flex">
<a
v-if="addon.author.link"
:href="addon.author.link"
class="text-current text-sm opacity-60 hover:opacity-100"
target="_blank"
>{{ addon.author.name }}</a>
<div v-else class="text-current text-sm opacity-60 hover:opacity-100">
{{ addon.author.name }}
</div>
<div class="flex-auto" />
<a
v-if="addon.id"
:href="`https://npmjs.com/package/${addon.id}`"
class="ml-2 text-current opacity-40 text-sm hover:opacity-100"
target="_blank"
>
<simple-icons-npm />
</a>
<a
v-if="addon.repo"
:href="addon.repo"
class="ml-2 text-current opacity-40 text-sm hover:opacity-100"
target="_blank"
>
<simple-icons-github />
</a>
</div>
</a>
</template>
================================================
FILE: docs/.vitepress/theme/components/Demo.vue
================================================
<script setup lang="ts">
import type { SlidevMarkdown } from '@slidev/types'
import Center from '@slidev/client/layouts/center.vue'
import Default from '@slidev/client/layouts/default.vue'
import { parseSync } from '@slidev/parser'
import Cover from '@slidev/theme-default/layouts/cover.vue'
import Markdown from 'markdown-it'
import TypeIt from 'typeit'
import { onMounted, ref, watch } from 'vue'
import DemoEditor from './DemoEditor.vue'
import DemoSlide from './DemoSlide.vue'
import SlideContainer from './SlideContainer.vue'
import '@slidev/client/styles/layouts-base.css'
import '@slidev/theme-default/styles/layouts.css'
const page = ref(0)
const paused = ref(false)
const completed = ref(false)
const code = ref('')
const info = ref<SlidevMarkdown>()
const block = ref<HTMLPreElement>()
const markdown = new Markdown()
function getLayout(id: number) {
const name = info.value?.slides?.[id]?.frontmatter?.layout || 'default'
return name === 'cover'
? Cover
: name === 'center'
? Center
: Default
}
watch([code, paused], () => {
if (paused.value)
return
try {
info.value = parseSync(code.value, '')
}
catch {
}
})
function getAttrs(id: number) {
return info.value?.slides?.[id]?.frontmatter
}
function getHTML(id: number) {
const content = info?.value?.slides?.[id]?.content
if (!content)
return ''
return markdown.render(content)
}
function pause() {
paused.value = true
}
function resume() {
paused.value = false
}
const COVER_URL = 'https://sli.dev/demo-cover.png'
if (typeof window !== 'undefined') {
const img1 = new Image()
img1.src = COVER_URL
}
function play() {
code.value = ''
block.value!.innerHTML = ''
completed.value = false
// @ts-expect-error wrong types provided by TypeIt
new TypeIt(block.value!, {
speed: 50,
startDelay: 900,
afterStep: () => {
// eslint-disable-next-line unicorn/prefer-dom-node-text-content
code.value = JSON.parse(JSON.stringify(block.value!.innerText.replace('|', '')))
},
afterComplete: () => {
setTimeout(() => completed.value = true, 300)
},
})
.type('<br><span class="token title"># Welcome to Slidev!</span><br><br>', { delay: 400 })
.type('Presentation Slides for Developers', { delay: 400 })
.move(null, { to: 'START', speed: 0 })
.type('<br>')
.move(null, { to: 'START' })
.exec(pause)
.type('<span class="token punctuation">---<br><br>---</span>')
.move(-4)
.type('<span class="token tag">layout:</span> center')
.exec(resume)
.pause(1000)
.exec(pause)
.delete(6, { delay: 100, speed: 50 })
.type('cover')
.exec(resume)
.exec(pause)
.pause(1000)
.type('<br>')
.type('<span class="token tag">background:</span> ', { delay: 200 })
.type(COVER_URL, { speed: 0 })
.exec(resume)
.pause(1000)
.move(null, { to: 'END', speed: 0 })
.exec(pause)
.type('<br><br><span class="token punctuation">---</span><br><br>', { delay: 400 })
.exec(resume)
.exec(() => setTimeout(() => page.value = 1))
.type('<span class="token title"># Page 2</span><br><br>', { delay: 400 })
.type('- 📄 Write slides in a single Markdown file<br>', { delay: 800 })
.type('- 🌈 Themes, code blocks, interactive components<br>', { delay: 800 })
.type('- 😎 Read the docs to learn more!', { delay: 800 })
.exec(() => setTimeout(() => page.value = 0))
.go()
}
onMounted(play)
</script>
<template>
<div>
<DemoEditor>
<div class="text-sm opacity-50 text-center">
./slides.md
</div>
<div v-if="completed" class="absolute text-xs right-1 top-1 icon-btn opacity-50" title="Replay" @click="play()">
<div class="i-carbon:reset" />
</div>
<div class="language-md !bg-transparent px4 py1">
<pre ref="block" class="text-left whitespace-pre-wrap font-mono bg-transparent" />
</div>
</DemoEditor>
<DemoSlide class="text-left">
<div
class="flex h-full dark:bg-[#181819] transition-transform transform duration-500"
style="width: 200%"
:class="page === 1 ? '-translate-x-1/2' : ''"
>
<SlideContainer class="w-full h-full">
<component :is="getLayout(0)" v-bind="getAttrs(0)">
<div v-html="getHTML(0)" />
</component>
</SlideContainer>
<SlideContainer class="w-full h-full">
<component :is="getLayout(1)" v-bind="getAttrs(1)">
<div v-html="getHTML(1)" />
</component>
</SlideContainer>
</div>
<div
class="absolute left-2 bottom-1 flex text-gray-200"
opacity="0 hover:100"
>
<div class="icon-btn" :class="{ disabled: page === 0 }" @click="page = 0">
<div class="i-carbon:chevron-left" />
</div>
<div class="icon-btn" :class="{ disabled: page === 1 }" @click="page = 1">
<div class="i-carbon:chevron-right" />
</div>
</div>
</DemoSlide>
</div>
</template>
<style>
.slidev-layout ul {
padding: 0;
}
.slidev-layout li {
line-height: 2.4em;
}
</style>
================================================
FILE: docs/.vitepress/theme/components/DemoEditor.vue
================================================
<template>
<div class="demo-editor">
<slot />
</div>
</template>
<style lang="postcss">
.demo-editor {
font-size: 1em;
line-height: 1.2em;
min-height: 450px;
margin-top: 25px;
padding: 10px;
border-radius: 7px;
position: relative;
box-shadow:
0 0 0 1px rgba(0, 0, 0, 0.05),
0 0 30px 1px rgba(0, 0, 0, 0.15);
}
.dark .demo-editor {
@apply bg-[#161618] border border-gray-400 border-opacity-10;
}
.demo-editor::after {
content: '';
position: absolute;
top: 12px;
left: 10px;
width: 12px;
height: 12px;
background: #f95c5b;
border-radius: 100%;
box-shadow:
0 0 0 1px #da3d42,
22px 0 0 0 #fabe3b,
22px 0 0 1px #ecb03e,
44px 0 0 0 #38cd46,
44px 0 0 1px #2eae32;
}
</style>
================================================
FILE: docs/.vitepress/theme/components/DemoSlide.vue
================================================
<template>
<div class="slide">
<div class="aspect-16/9" />
<div class="absolute top-0 left-0 right-0 bottom-0">
<slot />
</div>
</div>
</template>
<style lang="postcss">
.slide {
background: var(--c-bg);
font-size: 1em;
line-height: 1.2em;
border-radius: 7px;
position: relative;
box-shadow:
0 0 0 1px rgba(0, 0, 0, 0.05),
0 0 30px 1px rgba(0, 0, 0, 0.15);
@apply mt-4 transform translate-x-20 -translate-y-20 overflow-hidden;
}
.dark .slide {
@apply bg-gray-400 bg-opacity-5 border border-gray-400 border-opacity-10;
}
</style>
================================================
FILE: docs/.vitepress/theme/components/Environment.vue
================================================
<script setup lang="ts">
defineProps<{ type: 'node' | 'client' | 'both' }>()
</script>
<template>
<details class="p4 mt-4 rounded-lg bg-gray-400 bg-opacity-10">
<summary class="outline-none !m0 select-none">
Environment:
<span class="capitalize font-bold" :class="type === 'node' ? 'text-orange-400' : 'text-green-400'">{{ type }}</span>
</summary>
<div class="pt2 opacity-75">
<span v-if="type === 'both'">
This setup function will run on <b>both</b> Node.js and client side. Avoid using Node.js or DOM API to prevent runtime errors.
</span>
<span v-else-if="type === 'node'">
This setup function will only run on Node.js environment, you can have access to Node's API.
</span>
<span v-else-if="type === 'client'">
This setup function will only run on client side. Make sure the browser compatibility when importing packages.
</span>
</div>
</details>
</template>
================================================
FILE: docs/.vitepress/theme/components/FeatureTag.vue
================================================
<script setup lang="ts">
import { useData, withBase } from 'vitepress'
import { computed } from 'vue'
const props = defineProps<{
tag: string
removable?: boolean
noclick?: boolean
}>()
const emit = defineEmits(['remove'])
const { isDark } = useData()
function getHashColorFromString(
name: string,
opacity: number | string = 1,
) {
name += 'salt'
let hash = 0
for (let i = 0; i < name.length; i++)
hash = name.charCodeAt(i) + ((hash << 5) - hash)
const h = hash % 360
return getHsla(h, opacity)
}
function getHsla(
hue: number,
opacity: number | string = 1,
) {
const saturation = isDark.value ? 50 : 65
const lightness = isDark.value ? 60 : 40
return `hsla(${hue}, ${saturation}%, ${lightness}%, ${opacity})`
}
const formattedTag = computed(() => {
let tag = props.tag
if (tag === tag.toUpperCase()) {
return tag
}
tag = tag.replace(/\b(API|CLI)\b/gi, m => m.toUpperCase())
.replace(/-/g, ' ')
return tag[0].toUpperCase() + tag.slice(1)
})
const colors = computed(() => {
return [
getHashColorFromString(props.tag),
getHashColorFromString(props.tag, 0.7),
getHashColorFromString(props.tag, 0.5),
getHashColorFromString(props.tag, 0.2),
getHashColorFromString(props.tag, 0.1),
]
})
</script>
<template>
<a v-if="props.removable" class="feature-tag flex gap-1 items-center">
{{ formattedTag }}
<button class="flex items-center op-70 hover:bg-gray-200/20 hover:op90 rounded-full mr--1" @click="emit('remove')">
<div class="i-carbon:close" />
</button>
</a>
<span v-else-if="props.noclick" class="feature-tag">
{{ formattedTag }}
</span>
<a v-else class="feature-tag" :href="withBase(`/features/#tags=${tag}`)">
{{ formattedTag }}
</a>
</template>
<style scoped>
.feature-tag {
--uno: 'text-sm px-2 py-.5 rounded-md select-none !decoration-none border border-solid h-max';
background-color: v-bind('colors[4]');
color: v-bind('colors[0]') !important;
border-color: v-bind('colors[3]');
}
.feature-tag:hover {
background-color: v-bind('colors[3]');
}
</style>
================================================
FILE: docs/.vitepress/theme/components/FeaturesAnimation.vue
================================================
<script setup lang="ts">
import Inner from './FeaturesAnimationInner.vue'
</script>
<template>
<div class="outer">
<Inner class="inner" />
</div>
</template>
<style scoped>
</style>
================================================
FILE: docs/.vitepress/theme/components/FeaturesAnimationInner.vue
================================================
<script setup lang="ts">
import type { Feature } from '../../../features/index.data'
import { withBase } from 'vitepress'
import { computed, ref } from 'vue'
import { data as features } from '../../../features/index.data'
const rows = 6
const offsetMap = Array.from({ length: rows }, () => Math.floor(Math.random() * 200))
const featuresArr = Object.values(features) as Feature[]
const groupedFeatures = computed(() => {
const res: Feature[][] = Array.from({ length: rows }, () => [])
for (let i = 0; i < featuresArr.length; i++) {
res[i % rows].push(featuresArr[i])
}
return res
})
const round = ref(0)
</script>
<template>
<div class="relative w-full x-6 overflow-auto">
<div class="flex flex-col gap-4 ml--50">
<div v-for="group, i in groupedFeatures" :key="i" class="flex gap-4">
<div :style="{ minWidth: `${offsetMap[i]}px` }" />
<template v-for="r in [round, round + 1, round + 2]" :key="r">
<a
v-for="feature, j in group" :key="j" :href="withBase(feature.link)"
class="px-3 py-2 rounded bg-$vp-c-bg-elv min-w-max !decoration-none"
>
{{ feature.title }}
</a>
</template>
</div>
</div>
<div class="absolute left-0 top-0 bottom-0 w-10 backdrop-blur" />
</div>
</template>
================================================
FILE: docs/.vitepress/theme/components/FeaturesOverview.vue
================================================
<script setup lang="ts">
import type { Feature } from '../../../features/index.data'
import { withBase } from 'vitepress'
defineProps<{
features: Feature[]
}>()
</script>
<template>
<div class="features-grid mt-4">
<a
v-for="feature in features"
:key="feature.name"
flex flex-col h-full p4 gap-3 rounded-md
:href="withBase(feature.link)"
>
<div font-bold text-wrap leading-5> {{ feature.title }} </div>
<div text-wrap leading-5 op-75 overflow-hidden text-sm> {{ feature.description }} </div>
<div flex-grow />
<div flex gap-1 pointer-events-auto>
<FeatureTag v-for="tag in feature.tags" :key="tag" :tag />
</div>
</a>
</div>
</template>
<style scoped>
.features-grid {
display: grid;
grid-gap: 0.75rem;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
.features-grid > a {
color: var(--vp-c-text-1);
text-decoration: none;
transition:
background-color 0.25s,
color 0.25s;
background-color: var(--vp-c-bg-soft);
}
.features-grid > a:hover {
color: var(--vp-c-brand-1);
background-color: var(--vp-c-default-soft);
}
</style>
================================================
FILE: docs/.vitepress/theme/components/LandingPage.vue
================================================
<template>
<div class="xl:grid xl:grid-cols-[3fr_4fr] gap-4" min-h-80vh px8 xl:px20 py5 max-w-100vw xl:max-w-450 mxa>
<div flex="~ col items-center justify-center" min-h-150>
<h1 hidden>
Slidev
</h1>
<img src="/logo-title.png" alt="Slidev" w-80 xl:w-100 xl:mt--35>
<h2 text-3xl mt--5 op80 text-center>
Presentation Slides for Developers
</h2>
<div flex="~ gap-3 justify-center" p4 mt-5>
<a href="/guide/" class="bg-$vp-c-brand-3 text-white! no-underline! px5 py3 text-xl font-bold rounded-xl hover:bg-$vp-c-brand-1 h-max">Get Started</a>
<a href="/guide/why" class="bg-$vp-c-gray-1 text-white! no-underline! px5 py3 text-xl font-bold rounded-2xl hover:bg-$vp-c-brand-1 h-max">Why</a>
</div>
</div>
<div flex>
<div w-180 ma>
<ClientOnly>
<Demo />
</ClientOnly>
</div>
</div>
</div>
</template>
================================================
FILE: docs/.vitepress/theme/components/Layout.vue
================================================
<script setup lang="ts">
import { Dropdown } from 'floating-vue'
import { useRoute } from 'vitepress'
import VPMenuLink from 'vitepress/dist/client/theme-default/components/VPMenuLink.vue'
import DefaultTheme from 'vitepress/theme'
import FeatureTag from './FeatureTag.vue'
const { Layout } = DefaultTheme
const route = useRoute()
</script>
<template>
<Layout>
<template #nav-bar-title-after>
<a @click.prevent>
<Dropdown :triggers="['hover', 'click']" :popper-triggers="['hover']" theme="twoslash" popper-class="z-1000">
<Badge class="scale-80 translate-x--1 select-none">
New Docs!
</Badge>
<template #popper>
<div class="p3 text-sm">
<p>
You are viewing the new Slidev documentation.
</p>
<p>
The old one is available
<a href="https://docs-legacy.sli.dev/" class="underline text-primary" target="_blank">here</a>.
</p>
</div>
</template>
</Dropdown>
</a>
</template>
<template #sidebar-nav-after>
<div flex="~ col gap-2">
<div v-if="route.data?.frontmatter?.tags" class="bg-$vp-c-bg-soft p4 rounded-lg" flex="~ col gap-2">
<div font-bold text-sm op75>
Tags
</div>
<div flex="~ wrap gap-2">
<FeatureTag v-for="tag in route.data.frontmatter.tags" :key="tag" :tag="tag" />
</div>
</div>
<div v-if="route.data?.frontmatter?.since" class="bg-$vp-c-bg-soft px2 pb2 rounded-lg" flex="~ col gap-1">
<div font-bold text-sm op75 px2 pt4>
Since
</div>
<VPMenuLink
:item="{
text: route.data.frontmatter.since,
link: `https://github.com/slidevjs/slidev/releases/tag/${route.data.frontmatter.since}`,
}"
/>
</div>
</div>
</template>
</Layout>
</template>
================================================
FILE: docs/.vitepress/theme/components/LinkCard.vue
================================================
<script setup lang="ts">
import { withBase } from 'vitepress'
import { computed } from 'vue'
import { resolveLink } from '../../utils'
const props = defineProps<{
link: string
}>()
const resolved = computed(() => resolveLink(props.link))
</script>
<template>
<div class="sr-only">
{{ resolved.title }}
</div>
<ClientOnly>
<a class="link-card" :href="withBase(resolved.url)">
<div class="title">
<div>{{ resolved.title }}</div>
<div flex-grow />
<div flex gap-1>
<FeatureTag v-for="tag in resolved.tags" :key="tag" :tag />
</div>
</div>
<div class="description">
{{ resolved.descripton }}
</div>
</a>
</ClientOnly>
</template>
<style scoped>
.link-card {
--uno: 'block my-4 pl-8 pr-6 py-6 rounded-lg bg-$vp-c-bg-soft flex flex-col gap-2';
border: 2px solid var(--vp-c-bg-soft);
transition:
color 0.5s,
background-color 0.5s;
text-decoration: none;
}
.link-card:hover {
border-color: var(--vp-c-brand-soft);
transition: border-color 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}
.title {
font-size: 18px;
line-height: 1.4;
letter-spacing: -0.02em;
display: flex;
color: var(--vp-c-brand-1);
}
.description {
margin-bottom: 0;
color: var(--vp-c-text-2);
transition: color 0.5s;
}
</style>
================================================
FILE: docs/.vitepress/theme/components/LinkInline.vue
================================================
<script setup lang="ts">
import { Tooltip } from 'floating-vue'
import { withBase } from 'vitepress'
import { computed } from 'vue'
import { resolveLink } from '../../utils'
const props = defineProps<{
link: string
}>()
const resolved = computed(() => resolveLink(props.link))
</script>
<template>
<span class="sr-only">{{ resolved.title }}</span>
<ClientOnly>
<Tooltip class="inline-block" theme="twoslash">
<a
:href="withBase(resolved.url)"
target="_blank"
class="bg-$vp-c-default-soft hover:bg-$vp-c-brand-soft rounded px2 py1 !decoration-none"
>
{{ resolved.title }}
</a>
<template #popper>
<a
:href="withBase(resolved.url)"
target="_blank"
flex flex-col p4 gap-2 max-w-100
>
<div flex gap-2>
<div>
{{ resolved.title }}
</div>
<div flex-grow />
<FeatureTag v-for="tag in resolved.tags" :key="tag" :tag :noclick="resolved.kind !== 'features'" text-xs />
</div>
<div op75 text-sm>
{{ resolved.descripton }}
</div>
</a>
</template>
</Tooltip>
</ClientOnly>
</template>
================================================
FILE: docs/.vitepress/theme/components/SeeAlso.vue
================================================
<script setup lang="ts">
defineProps<{
links: string[]
}>()
</script>
<template>
<div class="op-90 mb--1">
See also:
</div>
<LinkCard v-for="link in links" :key="link" :link="link" />
</template>
================================================
FILE: docs/.vitepress/theme/components/ShowCaseInfo.vue
================================================
<script setup lang="ts">
import type { ShowCaseInfo } from '../../showcases'
defineProps<{
info: ShowCaseInfo
}>()
</script>
<template>
<div class="flex flex-col gap-2">
<div class="block w-full relative aspect-16/9">
<a
class="absolute top-0 bottom-0 left-0 right-0 overflow-hidden"
border="~ rounded gray-400 opacity-20"
:href="info.slidesLink"
target="_blank"
>
<img :src="info.cover">
</a>
</div>
<div class="font-bold line-clamp-2">
{{ info.title }}
</div>
<div class="text-current text-xs opacity-75 whitespace-nowrap overflow-hidden overflow-ellipsis">
{{ info.at }}
</div>
<div class="flex-auto" />
<div class="flex">
<a
v-if="info.author.link"
:href="info.author.link"
class="text-current! text-sm opacity-50 whitespace-nowrap overflow-hidden overflow-ellipsis"
target="_blank"
>{{ info.author.name }}</a>
<div v-else class="text-current! text-sm opacity-50 whitespace-nowrap overflow-hidden overflow-ellipsis">
{{ info.author.name }}
</div>
<div class="flex-auto" />
<a
v-if="info.videoLink"
:href="info.videoLink"
class="ml-2 text-current! opacity-20 hover:opacity-100 hover:text-[#cb3837]"
target="_blank"
>
<div class="i-carbon:video" />
</a>
<a
v-if="info.slidesLink"
:href="info.slidesLink"
class="ml-2 text-current! opacity-20 hover:opacity-100 hover:text-[#cb3837]"
target="_blank"
>
<div class="i-carbon:presentation-file" />
</a>
<a
v-if="info.sourceLink"
:href="info.sourceLink"
class="ml-2 text-current! opacity-20 hover:opacity-100"
target="_blank"
>
<div class="i-carbon:logo-github" />
</a>
</div>
</div>
</template>
================================================
FILE: docs/.vitepress/theme/components/ShowCases.vue
================================================
<script setup lang="ts">
import { showcases } from '../../showcases'
</script>
<template>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
<ShowCaseInfo v-for="(info, idx) of showcases" :key="idx" :info="info" />
</div>
</template>
================================================
FILE: docs/.vitepress/theme/components/SlideContainer.vue
================================================
<script setup lang="ts">
import { useElementSize } from '@vueuse/core'
import { computed, ref } from 'vue'
const slideAspect = 16 / 9
const slideWidth = 980
const slideHeight = 980 * 9 / 16
const root = ref<HTMLDivElement>()
const element = useElementSize(root)
const width = computed(() => element.width.value)
const height = computed(() => element.height.value)
const screenAspect = computed(() => width.value / height.value)
const scale = computed(() => {
if (screenAspect.value < slideAspect)
return width.value / slideWidth
return height.value * slideAspect / slideWidth
})
const style = computed(() => ({
height: `${slideHeight}px`,
width: `${slideWidth}px`,
transform: `translate(-50%, -50%) scale(${scale.value})`,
}))
</script>
<template>
<div ref="root" class="slide-container">
<div class="slide-content" :style="style">
<slot />
</div>
<slot name="controls" />
</div>
</template>
<style lang="postcss">
.slide-container {
@apply relative overflow-hidden;
}
.slide-content {
@apply relative overflow-hidden bg-main absolute left-1/2 top-1/2;
}
</style>
================================================
FILE: docs/.vitepress/theme/components/TheTweet.vue
================================================
<!--
A simple wrapper for embedded Tweet
Usage:
<TheTweet id="20" />
-->
<script setup lang="ts">
import { isClient, useScriptTag } from '@vueuse/core'
import { getCurrentInstance, onMounted, ref } from 'vue'
import { isDark } from '../composables/dark'
const props = defineProps<{
id: string | number
scale?: string | number
conversation?: string
}>()
const tweet = ref<HTMLElement | null>()
const vm = getCurrentInstance()!
const loaded = ref(false)
async function create() {
// @ts-expect-error Global variable
if (!window.twttr?.widgets?.createTweet)
return
// @ts-expect-error Global variable
await window.twttr.widgets.createTweet(
props.id.toString(),
tweet.value,
{
theme: isDark.value ? 'dark' : 'light',
conversation: props.conversation || 'none',
},
)
loaded.value = true
}
if (isClient) {
// @ts-expect-error Global variable
if (window?.twttr?.widgets?.createTweet) {
onMounted(create)
}
else {
useScriptTag(
'https://platform.twitter.com/widgets.js',
() => {
if (vm.isMounted)
create()
else
onMounted(create, vm)
},
{ async: true },
)
}
}
</script>
<template>
<div class="w-full flex">
<div ref="tweet" class="mx-auto w-140">
<div v-if="!loaded" class="w-30 h-30 my-10px bg-gray-400 bg-opacity-10 rounded-lg flex opacity-50">
<div class="m-auto animate-pulse text-4xl">
<div class="i-carbon:logo-twitter" />
</div>
</div>
</div>
</div>
</template>
================================================
FILE: docs/.vitepress/theme/components/ThemeGallery.vue
================================================
<script setup lang="ts">
import { computed } from 'vue'
import { community, official } from '../../themes'
const props = defineProps({
collection: {
default: 'official',
},
})
const themes = computed(() => props.collection === 'official' ? official : community)
</script>
<template>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
<ThemeInfo v-for="theme of themes" :key="theme.id" :theme="theme" />
</div>
</template>
================================================
FILE: docs/.vitepress/theme/components/ThemeInfo.vue
================================================
<script setup lang="ts">
import type { ThemeInfo } from '../../themes'
import { isClient, useIntervalFn } from '@vueuse/core'
import { ref } from 'vue'
const props = defineProps<{
theme: ThemeInfo
}>()
const index = ref(0)
if (props.theme.previews.length > 1 && isClient) {
const { resume } = useIntervalFn(() => {
index.value = (index.value + 1) % props.theme.previews.length
}, 3000, { immediate: false })
// add random defer so they don't starts together
setTimeout(resume, Math.round(1000 * Math.random()))
}
</script>
<template>
<div>
<a
:href="theme.link || theme.repo"
target="_blank"
class="block mb-1.5 w-full overflow-hidden relative aspect-16/9 transition duration-300"
border="~ rounded gray-400 opacity-20"
hover="shadow-xl"
>
<img
v-for="url, idx in theme.previews"
:key="idx"
:src="url"
class="absolute top-0 bottom-0 left-0 right-0 transition-transform transform duration-500"
:style="{ transform: idx > index ? 'scale(1.05) translate(110%)' : 'scale(1.05) translate(0)' }"
>
</a>
<a :href="theme.link || theme.repo" class="font-bold !text-$vp-c-text-1 !decoration-none">
{{ theme.name }}
</a>
<div
class="text-current text-xs opacity-75 whitespace-nowrap overflow-hidden text-ellipsis"
:title="theme.description"
>
{{ theme.description }}
</div>
<div class="mt-2 flex">
<a
v-if="theme.author.link"
:href="theme.author.link"
class="text-current text-sm opacity-60 hover:opacity-100"
target="_blank"
>{{ theme.author.name }}</a>
<div v-else class="text-current text-sm opacity-60 hover:opacity-100">
{{ theme.author.name }}
</div>
<div class="flex-auto" />
<a
v-if="theme.id"
:href="`https://npmjs.com/package/${theme.id}`"
class="ml-2 text-current opacity-40 text-sm hover:opacity-100"
target="_blank"
>
<simple-icons-npm />
</a>
<a
v-if="theme.repo"
:href="theme.repo"
class="ml-2 text-current opacity-40 text-sm hover:opacity-100"
target="_blank"
>
<simple-icons-github />
</a>
</div>
</div>
</template>
================================================
FILE: docs/.vitepress/theme/composables/dark.ts
================================================
import { useDark } from '@vueuse/core'
export const isDark = useDark()
================================================
FILE: docs/.vitepress/theme/index.ts
================================================
import type { EnhanceAppContext } from 'vitepress'
import TwoSlash from '@shikijs/vitepress-twoslash/client'
import Theme from 'vitepress/theme'
import Layout from './components/Layout.vue'
import '@shikijs/vitepress-twoslash/style.css'
import './styles/vars.css'
import './styles/demo.css'
import './styles/custom.css'
import 'uno.css'
import 'virtual:group-icons.css'
export default {
extends: Theme,
enhanceApp({ app }: EnhanceAppContext) {
app.use(TwoSlash as any)
},
Layout,
}
================================================
FILE: docs/.vitepress/theme/styles/custom.css
================================================
.icon-btn {
--uno: inline-block cursor-pointer select-none important-outline-none;
--uno: opacity-75 transition duration-200 ease-in-out align-middle rounded p-2;
--uno: hover-(opacity-100 bg-gray-400 bg-opacity-10);
}
.icon-btn.disabled {
--uno: opacity-25 pointer-events-none;
}
.inline-icon-btn {
--uno: text-primary-deep;
--uno: inline-block rounded p-0.5 text-2xl align-middle;
--uno: border border-primary border-opacity-20 border-solid;
}
kbd {
--uno: border-rounded bg-$vp-c-gray-1 bg-opacity-10 px-1 py-.5;
}
[data-tweet-id] {
border-radius: 13px;
}
================================================
FILE: docs/.vitepress/theme/styles/demo.css
================================================
html:not(.dark) {
--prism-foreground: #393a34;
--prism-background: #fafafa;
--prism-inline-background: #f5f5f5;
--prism-comment: #a0ada0;
--prism-string: #b56959;
--prism-literal: #2f8a89;
--prism-number: #296aa3;
--prism-keyword: #1c6b48;
--prism-function: #6c7834;
--prism-boolean: #296aa3;
--prism-constant: #a65e2b;
--prism-deleted: #a14f55;
--prism-class: #2993a3;
--prism-builtin: #ab5959;
--prism-property: #b58451;
--prism-namespace: #b05a78;
--prism-punctuation: #8e8f8b;
--prism-decorator: #bd8f8f;
--prism-regex: #ab5e3f;
--prism-json-property: #698c96;
}
html.dark {
--prism-scheme: dark;
--prism-foreground: #d4cfbf;
--prism-background: #181818;
--prism-comment: #758575;
--prism-string: #d48372;
--prism-literal: #429988;
--prism-keyword: #4d9375;
--prism-boolean: #6394bf;
--prism-number: #6394bf;
--prism-variable: #c2b36e;
--prism-function: #a1b567;
--prism-deleted: #bc6066;
--prism-class: #54b1bf;
--prism-builtin: #e0a569;
--prism-property: #dd8e6e;
--prism-namespace: #db889a;
--prism-punctuation: #858585;
--prism-decorator: #bd8f8f;
--prism-regex: #ab5e3f;
--prism-json-property: #6b8b9e;
--prism-line-number: #888888;
--prism-line-number-gutter: #eeeeee;
--prism-line-highlight-background: #444444;
--prism-selection-background: #444444;
--prism-inline-background: theme('colors.dark.300');
}
.token.title {
color: var(--prism-keyword);
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: var(--prism-comment);
font-style: var(--prism-comment-style);
}
.token.namespace {
color: var(--prism-namespace);
}
.token.interpolation {
color: var(--prism-interpolation);
}
.token.string {
color: var(--prism-string);
}
.token.punctuation {
color: var(--prism-punctuation);
}
.token.operator {
color: var(--prism-operator);
}
.token.keyword.module,
.token.keyword.control-flow {
color: var(--prism-keyword-control);
}
.token.url,
.token.symbol,
.token.inserted {
color: var(--prism-symbol);
}
.token.constant {
color: var(--prism-constant);
}
.token.string.url {
text-decoration: var(--prism-url-decoration);
}
.token.boolean,
.language-json .token.boolean {
color: var(--prism-boolean);
}
.token.number,
.language-json .token.number {
color: var(--prism-number);
}
.token.variable {
color: var(--prism-variable);
}
.token.keyword {
color: var(--prism-keyword);
}
.token.atrule,
.token.attr-value,
.token.selector {
color: var(--prism-selector);
}
.token.function {
color: var(--prism-function);
}
.token.deleted {
color: var(--prism-deleted);
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.class-name {
color: var(--prism-class);
}
.token.tag,
.token.builtin {
color: var(--prism-builtin);
}
.token.attr-name,
.token.property,
.token.entity {
color: var(--prism-property);
}
.language-json .token.property {
color: var(--prism-json-property);
}
.token.regex {
color: var(--prism-regex);
}
.token.decorator,
.token.annotation {
color: var(--prism-decorator);
}
================================================
FILE: docs/.vitepress/theme/styles/vars.css
================================================
/**
* Customize default theme styling by overriding CSS variables:
* https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css
*/
/**
* Colors
*
* Each colors have exact same color scale system with 3 levels of solid
* colors with different brightness, and 1 soft color.
*
* - `XXX-1`: The most solid color used mainly for colored text. It must
* satisfy the contrast ratio against when used on top of `XXX-soft`.
*
* - `XXX-2`: The color used mainly for hover state of the button.
*
* - `XXX-3`: The color for solid background, such as bg color of the button.
* It must satisfy the contrast ratio with pure white (#ffffff) text on
* top of it.
*
* - `XXX-soft`: The color used for subtle background such as custom container
* or badges. It must satisfy the contrast ratio when putting `XXX-1` colors
* on top of it.
*
* The soft color must be semi transparent alpha channel. This is crucial
* because it allows adding multiple "soft" colors on top of each other
* to create a accent, such as when having inline code block inside
* custom containers.
*
* - `default`: The color used purely for subtle indication without any
* special meanings attched to it such as bg color for menu hover state.
*
* - `brand`: Used for primary brand colors, such as link text, button with
* brand theme, etc.
*
* - `tip`: Used to indicate useful information. The default theme uses the
* brand color for this by default.
*
* - `warning`: Used to indicate warning to the users. Used in custom
* container, badges, etc.
*
* - `danger`: Used to show error, or dangerous message to the users. Used
* in custom container, badges, etc.
* -------------------------------------------------------------------------- */
:root {
--vp-c-brand-1: #3ab9d4;
--vp-c-brand-2: #60c4db;
--vp-c-brand-3: #6fcce1;
--vp-c-brand-soft: #3ab9d450;
--vp-c-bg-alt: #f9f9f9;
--vp-font-family-mono: theme('fontFamily.mono');
}
.dark {
--vp-c-brand-1: #6fcce1;
--vp-c-brand-2: #60c4db;
--vp-c-brand-3: #3ab9d4;
--vp-c-brand-soft: #3ab9d450;
--vp-c-bg-alt: #18181b;
--vp-c-gutter: #8883;
}
:root {
--vp-c-default-1: var(--vp-c-gray-1);
--vp-c-default-2: var(--vp-c-gray-2);
--vp-c-default-3: var(--vp-c-gray-3);
--vp-c-default-soft: var(--vp-c-gray-soft);
--vp-c-tip-1: var(--vp-c-brand-1);
--vp-c-tip-2: var(--vp-c-brand-2);
--vp-c-tip-3: var(--vp-c-brand-3);
--vp-c-tip-soft: var(--vp-c-brand-soft);
}
:root {
-vp-c-text-1: rgba(42, 40, 47);
-vp-c-text-2: rgba(42, 40, 47, 0.78);
-vp-c-text-3: rgba(42, 40, 47, 0.56);
--black-text-1: rgba(42, 40, 47);
}
.dark {
--vp-c-text-1: rgba(255, 255, 245, 0.86);
--vp-c-text-2: rgba(235, 235, 245, 0.6);
--vp-c-text-3: rgba(235, 235, 245, 0.38);
}
/**
* Component: Button
* -------------------------------------------------------------------------- */
:root {
--vp-button-brand-border: transparent;
--vp-button-brand-text: var(--vp-c-white);
--vp-button-brand-bg: var(--vp-c-brand-1);
--vp-button-brand-hover-border: transparent;
--vp-button-brand-hover-text: var(--vp-c-white);
--vp-button-brand-hover-bg: var(--vp-c-brand-2);
--vp-button-brand-active-border: transparent;
--vp-button-brand-active-text: var(--vp-c-white);
--vp-button-brand-active-bg: var(--vp-c-brand-1);
}
.dark {
--vp-button-brand-text: var(--black-text-1);
--vp-button-brand-bg: var(--vp-c-brand-2);
--vp-button-brand-hover-text: var(--black-text-1);
--vp-button-brand-hover-bg: var(--vp-c-brand-1);
--vp-button-brand-active-text: var(--black-text-1);
--vp-button-brand-active-bg: var(--vp-c-brand-3);
}
/**
* Component: Home
* -------------------------------------------------------------------------- */
:root {
--vp-home-hero-name-color: var(--vp-c-brand-1);
}
@media (min-width: 640px) {
:root {
--vp-home-hero-image-filter: blur(56px);
}
}
@media (min-width: 960px) {
:root {
--vp-home-hero-image-filter: blur(72px);
}
}
/**
* Component: Custom Block
* -------------------------------------------------------------------------- */
:root {
--vp-custom-block-tip-border: transparent;
--vp-custom-block-tip-text: var(--vp-c-text-1);
--vp-custom-block-tip-bg: var(--vp-c-brand-soft);
--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);
}
/**
* Component: Algolia
* -------------------------------------------------------------------------- */
.DocSearch {
--docsearch-primary-color: var(--vp-c-brand-1) !important;
}
================================================
FILE: docs/.vitepress/themes.ts
================================================
export interface ThemeInfo {
id: string
name: string
description: string
previews: string[]
repo?: string
author: {
name: string
link?: string
}
link?: string
tags?: string[]
}
export const official: ThemeInfo[] = [
{
id: '@slidev/theme-default',
name: 'Default',
description: 'The minimalism default theme for Slidev',
author: {
name: 'Anthony Fu',
link: 'https://github.com/antfu',
},
repo: 'https://github.com/slidevjs/themes/tree/main/packages/theme-default',
previews: [
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-default/01.png',
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-default/02.png',
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-default/06.png',
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-default/08.png',
],
tags: [
'official',
'minimalism',
'dark',
'light',
],
},
{
id: '@slidev/theme-seriph',
name: 'Seriph',
description: 'A more formal looking theme using Serif fonts',
author: {
name: 'Anthony Fu',
link: 'https://github.com/antfu',
},
repo: 'https://github.com/slidevjs/themes/tree/main/packages/theme-seriph',
previews: [
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-seriph/01.png',
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-seriph/02.png',
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-seriph/03.png',
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-seriph/08.png',
],
tags: [
'official',
'minimalism',
'dark',
'light',
],
},
{
id: '@slidev/theme-apple-basic',
name: 'Apple Basic',
description: 'Inspired by the Basic Black/White theme from Apple Keynote',
author: {
name: 'Jeremy Meissner',
link: 'https://github.com/JeremyMeissner',
},
repo: 'https://github.com/slidevjs/themes/tree/main/packages/theme-apple-basic',
previews: [
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-apple-basic/01.png',
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-apple-basic/02.png',
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-apple-basic/03.png',
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-apple-basic/09.png',
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-apple-basic/11.png',
],
tags: [
'minimalism',
'dark',
'light',
],
},
{
id: '@slidev/theme-bricks',
name: 'Bricks',
description: 'Building bricks',
author: {
name: 'iiiiiiinès',
link: 'https://github.com/iiiiiiines',
},
repo: 'https://github.com/slidevjs/themes/tree/main/packages/theme-bricks',
previews: [
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-bricks/01.png',
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-bricks/04.png',
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-bricks/06.png',
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-bricks/05.png',
],
tags: [
'light',
],
},
{
id: '@slidev/theme-shibainu',
name: 'Shibainu',
description: 'Meow!',
author: {
name: 'iiiiiiinès',
link: 'https://github.com/iiiiiiines',
},
repo: 'https://github.com/slidevjs/themes/tree/main/packages/theme-shibainu',
previews: [
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-shibainu/01.png',
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-shibainu/03.png',
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-shibainu/04.png',
'https://cdn.jsdelivr.net/gh/slidevjs/themes@main/screenshots/theme-shibainu/09.png',
],
tags: [
'dark',
],
},
]
export const community: ThemeInfo[] = [
{
id: 'slidev-theme-geist',
name: 'Vercel',
description: 'A theme based on Vercel\'s design system.',
author: {
name: 'Nico Bachner',
link: 'https://github.com/nico-bachner',
},
repo: 'https://github.com/nico-bachner/slidev-theme-geist',
previews: [
'https://cdn.jsdelivr.net/gh/nico-bachner/slidev-theme-geist@main/example-export/01.png',
'https://cdn.jsdelivr.net/gh/nico-bachner/slidev-theme-geist@main/example-export/02.png',
'https://cdn.jsdelivr.net/gh/nico-bachner/slidev-theme-geist@main/example-export/03.png',
'https://cdn.jsdelivr.net/gh/nico-bachner/slidev-theme-geist@main/example-export/04.png',
'https://cdn.jsdelivr.net/gh/nico-bachner/slidev-theme-geist@main/example-export/05.png',
],
tags: [
'dark',
'light',
],
},
{
id: 'slidev-theme-light-icons',
name: 'Light Icons',
description: 'A simple, light and elegant theme for Slidev, combined together with creative layouts, custom components & fonts',
author: {
name: 'Pulkit Aggarwal',
link: 'https://github.com/BashCloud',
},
repo: 'https://github.com/lightvue/slidev-theme-light-icons',
previews: [
'https://cdn.jsdelivr.net/gh/lightvue/slidev-theme-light-icons@master/screenshot/1-layout-intro.png',
'https://cdn.jsdelivr.net/gh/lightvue/slidev-theme-light-icons@master/screenshot/2-layout-image-header-intro-light.png',
'https://cdn.jsdelivr.net/gh/lightvue/slidev-theme-light-icons@master/screenshot/3-layout-dynamic-image-light.png',
'https://cdn.jsdelivr.net/gh/lightvue/slidev-theme-light-icons@master/screenshot/5-layout-dynamic-image-light.png',
'https://cdn.jsdelivr.net/gh/lightvue/slidev-theme-light-icons@master/screenshot/7-layout-dynamic-image-light.png',
'https://cdn.jsdelivr.net/gh/lightvue/slidev-theme-light-icons@master/screenshot/8-layout-center-image-light.png',
'https://cdn.jsdelivr.net/gh/lightvue/slidev-theme-light-icons@master/screenshot/9-layout-dynamic-image-light.png',
'https://cdn.jsdelivr.net/gh/lightvue/slidev-theme-light-icons@master/screenshot/10-layout-left-image-light.png',
],
tags: [
'dark',
'light',
],
},
{
id: 'slidev-theme-eloc',
name: 'Eloc',
description: 'Focus on writing, present in a concise style.',
author: {
name: 'Amio',
link: 'https://github.com/amio',
},
repo: 'https://github.com/zthxxx/slides/tree/master/packages/slidev-theme-eloc',
previews: [
'https://cdn.jsdelivr.net/gh/zthxxx/slides@master/packages/slidev-theme-eloc/screenshot/01.png',
'https://cdn.jsdelivr.net/gh/zthxxx/slides@master/packages/slidev-theme-eloc/screenshot/02.png',
'https://cdn.jsdelivr.net/gh/zthxxx/slides@master/packages/slidev-theme-eloc/screenshot/03.png',
'https://cdn.jsdelivr.net/gh/zthxxx/slides@master/packages/slidev-theme-eloc/screenshot/04.png',
'https://cdn.jsdelivr.net/gh/zthxxx/slides@master/packages/slidev-theme-eloc/screenshot/05.png',
],
tags: [
'dark',
'light',
],
},
{
id: 'slidev-theme-purplin',
name: 'Purplin',
description: 'Theme with bar bottom component. Based on purple color',
author: {
name: 'Mauricio Martínez',
link: 'https://github.com/moudev',
},
repo: 'https://github.com/moudev/slidev-theme-purplin',
previews: [
'https://i.imgur.com/BX3TpEc.png',
'https://i.imgur.com/mqqRi1F.png',
'https://i.imgur.com/fwm2785.png',
'https://i.imgur.com/m8eemKt.png',
],
tags: [
'dark',
'light',
],
},
{
id: 'slidev-theme-unicorn',
name: 'Unicorn',
description: 'Based on Dawntraoz website design',
author: {
name: 'Alba Silvente',
link: 'https://github.com/dawntraoz',
},
repo: 'https://github.com/dawntraoz/slidev-theme-unicorn',
previews: [
'https://cdn.jsdelivr.net/gh/Dawntraoz/slidev-theme-unicorn@master/screenshots/dark-theme-intro.png',
'https://cdn.jsdelivr.net/gh/Dawntraoz/slidev-theme-unicorn@master/screenshots/light-theme-cover.png',
'https://cdn.jsdelivr.net/gh/Dawntraoz/slidev-theme-unicorn@master/screenshots/dark-theme-image-centered.png',
'https://cdn.jsdelivr.net/gh/Dawntraoz/slidev-theme-unicorn@master/screenshots/dark-theme-center-without-header-footer.png',
],
tags: [
'dark',
'light',
],
},
{
id: 'slidev-theme-zhozhoba',
name: 'Zhozhoba',
description: 'A zhozhoba theme for Slidev',
author: {
name: 'Bogenbai Bayzharassov',
link: 'https://github.com/thatoranzhevyy',
},
repo: 'https://github.com/thatoranzhevyy/slidev-theme-zhozhoba',
previews: [
'https://cdn.jsdelivr.net/gh/thatoranzhevyy/slidev-theme-zhozhoba@master/slides-export/01.png',
'https://cdn.jsdelivr.net/gh/thatoranzhevyy/slidev-theme-zhozhoba@master/.github/dark.png',
'https://cdn.jsdelivr.net/gh/thatoranzhevyy/slidev-theme-zhozhoba@master/slides-export/02.png',
'https://cdn.jsdelivr.net/gh/thatoranzhevyy/slidev-theme-zhozhoba@master/slides-export/03.png',
'https://cdn.jsdelivr.net/gh/thatoranzhevyy/slidev-theme-zhozhoba@master/slides-export/04.png',
],
tags: [
'dark',
'light',
],
},
{
id: 'slidev-theme-penguin',
name: 'Penguin',
description: 'A Penguin theme for Slidev',
author: {
name: 'Alvaro Saburido',
link: 'https://github.com/alvarosabu',
},
repo: 'https://github.com/alvarosabu/slidev-theme-penguin',
previews: [
'https://cdn.jsdelivr.net/gh/alvarosaburido/slidev-theme-penguin@master/screenshots/dark/01.png',
'https://cdn.jsdelivr.net/gh/alvarosaburido/slidev-theme-penguin@master/screenshots/light/02.png',
'https://cdn.jsdelivr.net/gh/alvarosaburido/slidev-theme-penguin@master/screenshots/light/06.png',
'https://cdn.jsdelivr.net/gh/alvarosaburido/slidev-theme-penguin@master/screenshots/light/05.png',
],
tags: [
'dark',
'light',
],
},
{
id: 'slidev-theme-vuetiful',
name: 'Vuetiful',
description: 'A Vue-inspired theme for Slidev',
author: {
name: 'Thorsten Lünborg',
link: 'https://github.com/LinusBorg',
},
repo: 'https://github.com/LinusBorg/slidev-theme-vuetiful',
previews: [
'https://cdn.jsdelivr.net/gh/LinusBorg/slidev-theme-vuetiful@main/screenshots/cover-alt.png',
'https://cdn.jsdelivr.net/gh/LinusBorg/slidev-theme-vuetiful@main/screenshots/section.png',
'https://cdn.jsdelivr.net/gh/LinusBorg/slidev-theme-vuetiful@main/screenshots/big-points.png',
'https://cdn.jsdelivr.net/gh/LinusBorg/slidev-theme-vuetiful@main/screenshots/quote.png',
],
tags: [
'dark',
'light',
],
},
{
id: 'slidev-theme-takahashi',
name: 'Takahashi',
description: 'A simple theme for Slidev',
author: {
name: 'Percy M.',
link: 'https://github.com/kecrily',
},
repo: 'https://github.com/kecrily/slidev-theme-takahashi',
previews: [
'https://cdn.jsdelivr.net/gh/kecrily/slidev-theme-takahashi@master/screenshots/01.png',
'https://cdn.jsdelivr.net/gh/kecrily/slidev-theme-takahashi@master/screenshots/02.png',
'https://cdn.jsdelivr.net/gh/kecrily/slidev-theme-takahashi@master/screenshots/03.png',
'https://cdn.jsdelivr.net/gh/kecrily/slidev-theme-takahashi@master/screenshots/04.png',
'https://cdn.jsdelivr.net/gh/kecrily/slidev-theme-takahashi@master/screenshots/05.png',
'https://cdn.jsdelivr.net/gh/kecrily/slidev-theme-takahashi@master/screenshots/06.png',
'https://cdn.jsdelivr.net/gh/kecrily/slidev-theme-takahashi@master/screenshots/07.png',
],
tags: [
'light',
],
},
{
id: 'slidev-theme-academic',
name: 'Academic',
description: 'Academic presentations with Slidev made simple',
author: {
name: 'Alexander Eble',
link: 'https://github.com/alexanderdavide',
},
repo: 'https://github.com/alexanderdavide/slidev-theme-academic',
previews: [
'https://cdn.jsdelivr.net/gh/alexanderdavide/slidev-theme-academic@assets/example-export/01.png',
'https://cdn.jsdelivr.net/gh/alexanderdavide/slidev-theme-academic@assets/example-export/02.png',
'https://cdn.jsdelivr.net/gh/alexanderdavide/slidev-theme-academic@assets/example-export/08.png',
'https://cdn.jsdelivr.net/gh/alexanderdavide/slidev-theme-academic@assets/example-export/04.png',
'https://cdn.jsdelivr.net/gh/alexanderdavide/slidev-theme-academic@assets/example-export/05.png',
'https://cdn.jsdelivr.net/gh/alexanderdavide/slidev-theme-academic@assets/example-export/06.png',
'https://cdn.jsdelivr.net/gh/alexanderdavide/slidev-theme-academic@assets/example-export/07.png',
],
tags: [
'dark',
'light',
],
},
{
id: 'slidev-theme-mokkapps',
name: 'Mokkapps',
description: 'A theme for my personal brand "Mokkapps"',
author: {
name: 'Michael Hoffmann',
link: 'https://github.com/mokkapps',
},
repo: 'https://github.com/mokkapps/slidev-theme-mokkapps',
previews: [
'https://cdn.jsdelivr.net/gh/mokkapps/slidev-theme-mokkapps@master/screenshots/dark/001.png',
'https://cdn.jsdelivr.net/gh/mokkapps/slidev-theme-mokkapps@master/screenshots/dark/002.png',
'https://cdn.jsdelivr.net/gh/mokkapps/slidev-theme-mokkapps@master/screenshots/dark/003.png',
'https://cdn.jsdelivr.net/gh/mokkapps/slidev-theme-mokkapps@master/screenshots/dark/004.png',
'https://cdn.jsdelivr.net/gh/mokkapps/slidev-theme-mokkapps@master/screenshots/dark/005.png',
'https://cdn.jsdelivr.net/gh/mokkapps/slidev-theme-mokkapps@master/screenshots/dark/006.png',
'https://cdn.jsdelivr.net/gh/mokkapps/slidev-theme-mokkapps@master/screenshots/dark/007.png',
'https://cdn.jsdelivr.net/gh/mokkapps/slidev-theme-mokkapps@master/screenshots/dark/008.png',
'https://cdn.jsdelivr.net/gh/mokkapps/slidev-theme-mokkapps@master/screenshots/dark/009.png',
'https://cdn.jsdelivr.net/gh/mokkapps/slidev-theme-mokkapps@master/screenshots/dark/010.png',
'https://cdn.jsdelivr.net/gh/mokkapps/slidev-theme-mokkapps@master/screenshots/dark/011.png',
],
tags: [
'dark',
'light',
],
},
{
id: 'slidev-theme-the-unnamed',
name: 'The unnamed',
description: 'A theme based on The unnamed VS Code theme',
author: {
name: 'Elio Struyf',
link: 'https://elio.dev',
},
repo: 'https://github.com/estruyf/slidev-theme-the-unnamed',
previews: [
'https://cdn.jsdelivr.net/gh/estruyf/slidev-theme-the-unnamed@main/assets/cover.png',
'https://cdn.jsdelivr.net/gh/estruyf/slidev-theme-the-unnamed@main/assets/about-me.png',
'https://cdn.jsdelivr.net/gh/estruyf/slidev-theme-the-unnamed@main/assets/default.png',
'https://cdn.jsdelivr.net/gh/estruyf/slidev-theme-the-unnamed@main/assets/section.png',
],
tags: [
'dark',
],
},
{
id: 'slidev-theme-dracula',
name: 'Dracula',
description: 'One the best dark theme meets slidev',
author: {
name: 'JD Solanki',
link: 'https://github.com/jd-solanki',
},
repo: 'https://github.com/jd-solanki/slidev-theme-dracula',
previews: [
'https://cdn.jsdelivr.net/gh/jd-solanki/slidev-theme-dracula/screenshots/screenshot-1.png',
'https://cdn.jsdelivr.net/gh/jd-solanki/slidev-theme-dracula/screenshots/screenshot-2.png',
'https://cdn.jsdelivr.net/gh/jd-solanki/slidev-theme-dracula/screenshots/screenshot-3.png',
'https://cdn.jsdelivr.net/gh/jd-solanki/slidev-theme-dracula/screenshots/screenshot-4.png',
'https://cdn.jsdelivr.net/gh/jd-solanki/slidev-theme-dracula/screenshots/screenshot-5.png',
],
tags: [
'dark',
'minimalism',
],
},
{
id: 'slidev-theme-frankfurt',
name: 'Frankfurt',
description: 'Inspired by the Beamer theme Frankfurt',
author: {
name: 'Mu-Tsun Tsai',
link: 'https://github.com/MuTsunTsai',
},
repo: 'https://github.com/MuTsunTsai/slidev-theme-frankfurt',
previews: [
'https://cdn.jsdelivr.net/gh/MuTsunTsai/slidev-theme-frankfurt/screenshots/01.png',
'https://cdn.jsdelivr.net/gh/MuTsunTsai/slidev-theme-frankfurt/screenshots/04.png',
'https://cdn.jsdelivr.net/gh/MuTsunTsai/slidev-theme-frankfurt/screenshots/06.png',
'https://cdn.jsdelivr.net/gh/MuTsunTsai/slidev-theme-frankfurt/screenshots/07.png',
],
tags: [
'dark',
'light',
],
},
{
id: 'slidev-theme-hep',
name: 'HEP',
description: 'Academic style for High Energy Physics',
author: {
name: 'Yulei ZHANG',
link: 'https://github.com/AvencastF',
},
repo: 'https://github.com/AvencastF/slidev-theme-hep',
previews: [
'https://cdn.jsdelivr.net/gh/avencastf/slidev-theme-hep/screenshot/001.png',
'https://cdn.jsdelivr.net/gh/avencastf/slidev-theme-hep/screenshot/004.png',
'https://cdn.jsdelivr.net/gh/avencastf/slidev-theme-hep/screenshot/006.png',
'https://cdn.jsdelivr.net/gh/avencastf/slidev-theme-hep/screenshot/008.png',
],
tags: [
'light',
],
},
{
id: 'slidev-theme-excali-slide',
name: 'Excali-slide',
description: 'A theme based on Excalidraw with animated highlighter effect',
author: {
name: 'Filip Hric',
link: 'https://github.com/filiphric',
},
repo: 'https://github.com/filiphric/slidev-theme-excali-slide',
previews: [
'https://raw.githubusercontent.com/filiphric/excali-slide/main/images/default_slide.png',
'https://raw.githubusercontent.com/filiphric/excali-slide/main/images/intro_slide.png',
],
tags: [
'dark',
'light',
],
},
{
id: 'slidev-theme-mint',
name: 'mint',
description: 'Slidev Theme Mint',
author: {
name: 'Alfatta Rezqa',
link: 'https://github.com/alfatta',
},
repo: 'https://github.com/alfatta/slidev-theme-mint',
previews: [
'https://cdn.jsdelivr.net/gh/alfatta/slidev-theme-mint/screenshot/1.png',
'https://cdn.jsdelivr.net/gh/alfatta/slidev-theme-mint/screenshot/2.png',
'https://cdn.jsdelivr.net/gh/alfatta/slidev-theme-mint/screenshot/3.png',
'https://cdn.jsdelivr.net/gh/alfatta/slidev-theme-mint/screenshot/4.png',
'https://cdn.jsdelivr.net/gh/alfatta/slidev-theme-mint/screenshot/5.png',
'https://cdn.jsdelivr.net/gh/alfatta/slidev-theme-mint/screenshot/6.png',
'https://cdn.jsdelivr.net/gh/alfatta/slidev-theme-mint/screenshot/7.png',
'https://cdn.jsdelivr.net/gh/alfatta/slidev-theme-mint/screenshot/8.png',
'https://cdn.jsdelivr.net/gh/alfatta/slidev-theme-mint/screenshot/9.png',
],
tags: [
'light',
'mint',
'green',
'cool',
],
},
{
id: 'slidev-theme-neversink',
name: 'neversink',
description: 'Slidev Theme Neversink',
author: {
name: 'Todd M. Gureckis',
link: 'https://github.com/gureckis',
},
repo: 'https://github.com/gureckis/slidev-theme-neversink',
previews: [
'https://gureckis.github.io/slidev-theme-neversink/screenshots/2.png',
'https://gureckis.github.io/slidev-theme-neversink/screenshots/6.png',
'https://gureckis.github.io/slidev-theme-neversink/screenshots/8.png',
'https://gureckis.github.io/slidev-theme-neversink/screenshots/15.png',
'https://gureckis.github.io/slidev-theme-neversink/screenshots/18.png',
'https://gureckis.github.io/slidev-theme-neversink/screenshots/22.png',
'https://gureckis.github.io/slidev-theme-neversink/screenshots/26.png',
'https://gureckis.github.io/slidev-theme-neversink/screenshots/34.png',
'https://gureckis.github.io/slidev-theme-neversink/screenshots/36.png',
'https://gureckis.github.io/slidev-theme-neversink/screenshots/38.png',
'https://gureckis.github.io/slidev-theme-neversink/screenshots/35.png',
],
tags: [
'light',
'academic',
'education',
],
},
{
id: 'slidev-theme-ktym4a',
name: 'ktym4a',
description: 'Based on ktym4a website design',
author: {
name: 'ktym4a',
link: 'https://github.com/ktym4a',
},
repo: 'https://github.com/ktym4a/slidev-theme-ktym4a',
previews: [
'https://cdn.jsdelivr.net/gh/ktym4a/slidev-theme-ktym4a@main/example-export/rotation/0.png',
'https://cdn.jsdelivr.net/gh/ktym4a/slidev-theme-ktym4a@main/example-export/rotation/1.png',
'https://cdn.jsdelivr.net/gh/ktym4a/slidev-theme-ktym4a@main/example-export/rotation/6.png',
'https://cdn.jsdelivr.net/gh/ktym4a/slidev-theme-ktym4a@main/example-export/rotation/7.png',
'https://cdn.jsdelivr.net/gh/ktym4a/slidev-theme-ktym4a@main/example-export/rotation/8.png',
'https://cdn.jsdelivr.net/gh/ktym4a/slidev-theme-ktym4a@main/example-export/single/0.png',
'https://cdn.jsdelivr.net/gh/ktym4a/slidev-theme-ktym4a@main/example-export/single/1.png',
'https://cdn.jsdelivr.net/gh/ktym4a/slidev-theme-ktym4a@main/example-export/single/3.png',
'https://cdn.jsdelivr.net/gh/ktym4a/slidev-theme-ktym4a@main/example-export/single/4.png',
],
tags: [
'dark',
'catppuccin',
],
},
{
id: 'slidev-theme-nord',
name: 'Nord',
description: 'Based on the Nord theme',
author: {
name: 'David Ollerhead',
link: 'https://github.com/oller',
},
repo: 'https://github.com/oller/slidev-theme-nord',
previews: [
'https://raw.githubusercontent.com/oller/slidev-theme-nord/HEAD/example-export/1.png',
'https://raw.githubusercontent.com/oller/slidev-theme-nord/HEAD/example-export/2.png',
'https://raw.githubusercontent.com/oller/slidev-theme-nord/HEAD/example-export/3.png',
'https://raw.githubusercontent.com/oller/slidev-theme-nord/HEAD/example-export/4.png',
'https://raw.githubusercontent.com/oller/slidev-theme-nord/HEAD/example-export/5.png',
'https://raw.githubusercontent.com/oller/slidev-theme-nord/HEAD/example-export/6.png',
'https://raw.githubusercontent.com/oller/slidev-theme-nord/HEAD/example-export/7.png',
],
tags: [
'dark',
'light',
'nord',
],
},
{
id: 'slidev-theme-scholarly',
name: 'Scholarly',
description: 'Based on the Nord theme',
author: {
name: 'Jiaxin Peng',
link: 'https://github.com/jxpeng98',
},
repo: 'https://github.com/jxpeng98/slidev-theme-scholarly',
previews: [
'https://raw.githubusercontent.com/jxpeng98/slidev-theme-scholarly/HEAD/images/themes/classic-blue/1.png',
'https://raw.githubusercontent.com/jxpeng98/slidev-theme-scholarly/HEAD/images/themes/oxford/1.png',
'https://raw.githubusercontent.com/jxpeng98/slidev-theme-scholarly/HEAD/images/themes/cambridge/1.png',
'https://raw.githubusercontent.com/jxpeng98/slidev-theme-scholarly/HEAD/images/themes/princeton/1.png',
'https://raw.githubusercontent.com/jxpeng98/slidev-theme-scholarly/HEAD/images/themes/classic-blue/2.png',
'https://raw.githubusercontent.com/jxpeng98/slidev-theme-scholarly/HEAD/images/themes/classic-blue/3.png',
'https://raw.githubusercontent.com/jxpeng98/slidev-theme-scholarly/HEAD/images/themes/classic-blue/4.png',
],
tags: [
'dark',
'light',
'academic',
'oxford',
'cambridge',
'princeton',
],
},
{
id: 'slidev-theme-field-manual',
name: 'Field Manual',
description: 'A 24-layout theme modeled on the style of vintage military field manuals',
author: {
name: 'PJ Doland',
link: 'https://github.com/pjdoland',
},
repo: 'https://github.com/pjdoland/slidev-theme-field-manual',
previews: [
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/1.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/2.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/3.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/4.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/5.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/6.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/7.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/8.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/9.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/10.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/11.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/12.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/13.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/14.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/15.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/16.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/17.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/18.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/19.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/20.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/21.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/22.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/23.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/24.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/25.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/26.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/27.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/28.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/29.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/30.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/31.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/32.jpg',
'https://raw.githubusercontent.com/pjdoland/slidev-theme-field-manual/main/screenshots/33.jpg',
],
tags: [
'light',
'dark',
'vintage',
'military',
],
},
// Add yours here!
{
id: '',
link: 'https://github.com/slidevjs/slidev/edit/main/docs/.vitepress/themes.ts',
name: 'Yours?',
description: 'Click here to submit your theme :)',
author: {
name: '',
},
previews: [
'/theme-placeholder.png',
],
},
]
================================================
FILE: docs/.vitepress/utils.ts
================================================
import { data as features } from '../features/index.data.js'
import { Advanced, Guides } from './pages'
function removeHash(link: string) {
const idx = link.lastIndexOf('#')
return idx < 0 ? link : link.slice(0, idx)
}
function getGuideTitle(id: string) {
return Guides.find(g => g.link.endsWith(`/${id}`))?.text ?? Advanced.find(g => g.link.endsWith(id))?.text ?? id
}
export function resolveLink(link: string): {
kind: 'external' | 'features' | 'guide'
url: string
title?: string
tags?: string[]
descripton?: string
} {
const [kind, nameWithHash] = link.split('/')
const name = removeHash(nameWithHash)
switch (kind) {
case 'http:':
case 'https:':
case 'mailto:':
return { kind: 'external', url: link }
case 'features': {
const feature = features[name]
if (!feature)
throw new Error(`Feature "${name}" not found.`)
return {
kind: 'features',
title: `✨ ${feature.title}`,
tags: feature.tags,
descripton: feature.description,
url: `/features/${nameWithHash}`,
}
}
case 'guide': {
return {
kind: 'guide',
title: `📖 ${getGuideTitle(name)}`,
tags: ['guide'],
descripton: 'Click to read this guide',
url: `/guide/${nameWithHash}`,
}
}
default:
throw new Error(`Invalid link: ${link}`)
}
}
================================================
FILE: docs/README.md
================================================
# [sli.dev](https://sli.dev)
Documentation for [Slidev](https://github.com/slidevjs/slidev)
## Translations
> [!WARNING]
>
> Translations with strikethroughs are no longer maintained. The content is outdated and not encouraged to refer.
| | Repo | Site | Maintainers |
| ------------------------- | ---------------------------------------------- | -------------------------------: | --------------------------------------------------------------------- |
| English | [docs](https://github.com/slidevjs/docs) | [sli.dev](https://sli.dev) | [@antfu](https://github.com/antfu) |
| 简体中文 | [docs-cn](https://github.com/slidevjs/docs-cn) | [cn.sli.dev](https://cn.sli.dev) | [@QC-L](https://github.com/QC-L) [@Ivocin](https://github.com/Ivocin) |
| <del>Français</del> | [docs-fr](https://github.com/slidevjs/docs-fr) | [fr.sli.dev](https://fr.sli.dev) | [@ArthurDanjou](https://github.com/ArthurDanjou) |
| <del>Español</del> | [docs-es](https://github.com/slidevjs/docs-es) | [es.sli.dev](https://es.sli.dev) | [@owlnai](https://github.com/owlnai) |
| <del>Русский</del> | [docs-ru](https://github.com/slidevjs/docs-ru) | [ru.sli.dev](https://ru.sli.dev) | [@xesjkeee](https://github.com/xesjkeee) |
| <del>Việt Nam</del> | [docs-vn](https://github.com/slidevjs/docs-vn) | [vn.sli.dev](https://vn.sli.dev) | [@bongudth](https://github.com/bongudth) |
| <del>Deutsch</del> | [docs-de](https://github.com/slidevjs/docs-de) | [de.sli.dev](https://de.sli.dev) | [@fabiankachlock](https://github.com/fabiankachlock) |
| <del>Português (BR)</del> | [docs-br](https://github.com/slidevjs/docs-br) | [br.sli.dev](https://br.sli.dev) | [@luisfelipesdn12](https://github.com/luisfelipesdn12) |
| <del>Ελληνικά</del> | [docs-el](https://github.com/slidevjs/docs-el) | [el.sli.dev](https://el.sli.dev) | [@GeopJr](https://github.com/GeopJr) |
| <del>日本語</del> | [docs-ja](https://github.com/slidevjs/docs-ja) | [ja.sli.dev](https://ja.sli.dev) | [@IkumaTadokoro](https://github.com/IkumaTadokoro) |
## Start Server Locally
```
npm i -g pnpm
pnpm i
pnpm run dev
```
And then visit `http://localhost:3000`
Or install the [Vite extension for VS Code](https://marketplace.visualstudio.com/items?itemName=antfu.vite) to edit side-by-side.
## Help on Translating
Please join our [Discord Server](https://chat.sli.dev) and contact the maintainers.
================================================
FILE: docs/builtin/cli.md
================================================
# Slidev CLI
`@slidev/cli` exposes a binary called `slidev` that you can use to develop, build, and export your slides.
## Prerequisites
To use the CLI, you can either install `@slidev/cli` globally or install it locally in your Node.js project. If you created your project with `npm init slidev`, the CLI is already installed locally.
::: warning
Usually `npx slidev` is not supported because the package name is actually `@slidev/cli`.
:::
The CLI options of the commands obey the following conventions:
- the value of the option can be passed after a space or a `=` character:
Example: `slidev --port 8080` is equivalent to `slidev --port=8080`
- `true` can be omitted for boolean options:
Example: `slidev --open` is equivalent to `slidev --open true`
::: info
If you use npm, please don't forget to add `--` before the options to pass them to Slidev:
```bash
npm run slidev -- --remote --port 8080 --open
```
:::
## `slidev [entry]` {#dev}
Start a local server for Slidev.
- `[entry]` (`string`, default: `slides.md`): path to the markdown file containing your slides.
Options:
- `--port`, `-p` (`number`, default: `3030`): port number.
- `--base` (`string`, default: `/`): base URL (see https://vitejs.dev/config/shared-options.html#base).
- `--open`, `-o` (`boolean`, default: `false`): open in the browser.
- `--remote [password]` (`string`): listen to the public host and enable remote control, if a value is passed then the presenter mode is private and only accessible by passing the given password in the URL query `password` parameter.
- `--bind` (`string`, default: `0.0.0.0`): specify which IP addresses the server should listen on in the remote mode.
- `--log` (`'error', 'warn', 'info', 'silent'`, default: `'warn'`): Log level.
- `--force`, `-f` (`boolean`, default: `false`): force the optimizer to ignore the cache and re-bundle.
- `--theme`, `-t` (`string`): override theme.
## `slidev build [entry]` {#build}
Build a hostable SPA. See <LinkInline link="guide/hosting" /> for more details.
- `[entry]` (`string`, default: `slides.md`): path to the slides markdown file.
Options:
- `--out`, `-o` (`string`, default: `dist`): output directory
- `--base` (`string`, default: `/`): base URL (see https://vitejs.dev/config/shared-options.html#base)
- `--download` (`boolean`, default: `false`): allow the download of the slides as a PDF inside the SPA
- `--theme`, `-t` (`string`): override theme
- `--without-notes` (`boolean`, default: `false`): exclude speaker notes from the SPA
## `slidev export [...entry]` {#export}
Export slides to PDF (or other format). See <LinkInline link="guide/exporting" /> for more details.
- `[entry]` (`string`, default: `slides.md`): path to the slides markdown entry.
Options:
- `--output` (`string`, default: use `exportFilename` (see https://sli.dev/custom/#frontmatter-configures) or use `[entry]-export`): path to the
gitextract_8zfy5w91/ ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ ├── config.yml │ │ └── feature_request.md │ ├── stale.yml │ └── workflows/ │ ├── autofix.yml │ ├── cr.yml │ ├── release.yml │ ├── smoke.yml │ └── test.yml ├── .gitignore ├── .vscode/ │ ├── extensions.json │ ├── launch.json │ └── settings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── cypress/ │ ├── e2e/ │ │ └── examples/ │ │ ├── basic.spec.ts │ │ ├── presenter-resizer.spec.ts │ │ └── smoke.spec.ts │ ├── fixtures/ │ │ └── basic/ │ │ ├── components/ │ │ │ ├── DecorateWithLi.vue │ │ │ ├── WrapInClicks.vue │ │ │ ├── WrapInClicksDecorate.vue │ │ │ └── WrapInComponentInClicks.vue │ │ ├── global.vue │ │ ├── package.json │ │ ├── slides.md │ │ ├── sub/ │ │ │ ├── page1.md │ │ │ └── page2.md │ │ └── vite.config.ts │ └── tsconfig.json ├── cypress.config.ts ├── demo/ │ ├── README.md │ ├── composable-vue/ │ │ ├── components/ │ │ │ ├── Connections.vue │ │ │ ├── DarkToggle.vue │ │ │ ├── Marker.vue │ │ │ ├── MarkerCore.vue │ │ │ ├── MarkerPattern.vue │ │ │ ├── MarkerTips.vue │ │ │ ├── NumBox.vue │ │ │ └── VueUse.vue │ │ ├── index.html │ │ ├── package.json │ │ ├── setup/ │ │ │ └── monaco.ts │ │ └── slides.md │ ├── starter/ │ │ ├── README.md │ │ ├── components/ │ │ │ └── Counter.vue │ │ ├── package.json │ │ ├── pages/ │ │ │ └── imported-slides.md │ │ ├── slides.md │ │ ├── snippets/ │ │ │ └── external.ts │ │ ├── style.css │ │ └── vite.config.ts │ └── vue-runner/ │ ├── package.json │ ├── setup/ │ │ ├── code-runners.ts │ │ └── shiki.ts │ └── slides.md ├── docs/ │ ├── .gitignore │ ├── .vitepress/ │ │ ├── addons.ts │ │ ├── config.ts │ │ ├── customizations.ts │ │ ├── pages.ts │ │ ├── showcases.ts │ │ ├── sidebar-gen.ts │ │ ├── theme/ │ │ │ ├── components/ │ │ │ │ ├── AddonGallery.vue │ │ │ │ ├── AddonInfo.vue │ │ │ │ ├── Demo.vue │ │ │ │ ├── DemoEditor.vue │ │ │ │ ├── DemoSlide.vue │ │ │ │ ├── Environment.vue │ │ │ │ ├── FeatureTag.vue │ │ │ │ ├── FeaturesAnimation.vue │ │ │ │ ├── FeaturesAnimationInner.vue │ │ │ │ ├── FeaturesOverview.vue │ │ │ │ ├── LandingPage.vue │ │ │ │ ├── Layout.vue │ │ │ │ ├── LinkCard.vue │ │ │ │ ├── LinkInline.vue │ │ │ │ ├── SeeAlso.vue │ │ │ │ ├── ShowCaseInfo.vue │ │ │ │ ├── ShowCases.vue │ │ │ │ ├── SlideContainer.vue │ │ │ │ ├── TheTweet.vue │ │ │ │ ├── ThemeGallery.vue │ │ │ │ └── ThemeInfo.vue │ │ │ ├── composables/ │ │ │ │ └── dark.ts │ │ │ ├── index.ts │ │ │ └── styles/ │ │ │ ├── custom.css │ │ │ ├── demo.css │ │ │ └── vars.css │ │ ├── themes.ts │ │ └── utils.ts │ ├── README.md │ ├── builtin/ │ │ ├── cli.md │ │ ├── components.md │ │ └── layouts.md │ ├── custom/ │ │ ├── config-code-runners.md │ │ ├── config-context-menu.md │ │ ├── config-fonts.md │ │ ├── config-highlighter.md │ │ ├── config-katex.md │ │ ├── config-mermaid-renderer.md │ │ ├── config-mermaid.md │ │ ├── config-monaco.md │ │ ├── config-parser.md │ │ ├── config-routes.md │ │ ├── config-shortcuts.md │ │ ├── config-transformers.md │ │ ├── config-unocss.md │ │ ├── config-vite.md │ │ ├── config-vue.md │ │ ├── directory-structure.md │ │ └── index.md │ ├── features/ │ │ ├── block-frontmatter.md │ │ ├── build-with-pdf.md │ │ ├── bundle-remote-assets.md │ │ ├── canvas-size.md │ │ ├── click-marker.md │ │ ├── code-block-line-numbers.md │ │ ├── code-block-max-height.md │ │ ├── code-groups.md │ │ ├── comark.md │ │ ├── direction-variant.md │ │ ├── draggable.md │ │ ├── drawing.md │ │ ├── eject-theme.md │ │ ├── frontmatter-merging.md │ │ ├── global-layers.md │ │ ├── icons.md │ │ ├── import-snippet.md │ │ ├── importing-slides.md │ │ ├── index.data.ts │ │ ├── index.md │ │ ├── latex.md │ │ ├── line-highlighting.md │ │ ├── mermaid.md │ │ ├── monaco-editor.md │ │ ├── monaco-run.md │ │ ├── monaco-write.md │ │ ├── notes-auto-ruby.md │ │ ├── og-image.md │ │ ├── plantuml.md │ │ ├── prettier-plugin.md │ │ ├── recording.md │ │ ├── remote-access.md │ │ ├── rough-marker.md │ │ ├── seo-meta.md │ │ ├── shiki-magic-move.md │ │ ├── side-editor.md │ │ ├── slide-hook.md │ │ ├── slide-scope-style.md │ │ ├── slot-sugar.md │ │ ├── timer.md │ │ ├── transform-component.md │ │ ├── twoslash.md │ │ ├── vscode-extension.md │ │ └── zoom-slide.md │ ├── guide/ │ │ ├── animations.md │ │ ├── component.md │ │ ├── exporting.md │ │ ├── faq.md │ │ ├── global-context.md │ │ ├── hosting.md │ │ ├── index.md │ │ ├── layout.md │ │ ├── syntax.md │ │ ├── theme-addon.md │ │ ├── ui.md │ │ ├── why.md │ │ ├── work-with-ai.md │ │ ├── write-addon.md │ │ ├── write-layout.md │ │ └── write-theme.md │ ├── index.md │ ├── netlify.toml │ ├── package.json │ ├── resources/ │ │ ├── addon-gallery.md │ │ ├── covers.md │ │ ├── learning.md │ │ ├── showcases.md │ │ └── theme-gallery.md │ ├── tsconfig.json │ ├── uno.config.ts │ └── vite.config.ts ├── eslint.config.js ├── netlify.toml ├── package.json ├── packages/ │ ├── client/ │ │ ├── .generated/ │ │ │ └── unocss-tokens.ts │ │ ├── App.vue │ │ ├── README.md │ │ ├── builtin/ │ │ │ ├── Arrow.vue │ │ │ ├── AutoFitText.vue │ │ │ ├── CodeBlockWrapper.vue │ │ │ ├── CodeGroup.vue │ │ │ ├── KaTexBlockWrapper.vue │ │ │ ├── LightOrDark.vue │ │ │ ├── Link.vue │ │ │ ├── Mermaid.vue │ │ │ ├── Monaco.vue │ │ │ ├── PlantUml.vue │ │ │ ├── PoweredBySlidev.vue │ │ │ ├── RenderWhen.vue │ │ │ ├── ShikiMagicMove.vue │ │ │ ├── SlideCurrentNo.vue │ │ │ ├── SlidesTotal.vue │ │ │ ├── SlidevVideo.vue │ │ │ ├── Toc.vue │ │ │ ├── TocList.vue │ │ │ ├── Transform.vue │ │ │ ├── Tweet.vue │ │ │ ├── VAfter.ts │ │ │ ├── VClick.ts │ │ │ ├── VClickGap.vue │ │ │ ├── VClicks.ts │ │ │ ├── VDrag.vue │ │ │ ├── VDragArrow.vue │ │ │ ├── VSwitch.ts │ │ │ └── Youtube.vue │ │ ├── composables/ │ │ │ ├── useClicks.ts │ │ │ ├── useDarkMode.ts │ │ │ ├── useDragElements.ts │ │ │ ├── useDrawings.ts │ │ │ ├── useEmbeddedCtrl.ts │ │ │ ├── useHideCursorIdle.ts │ │ │ ├── useIME.ts │ │ │ ├── useNav.ts │ │ │ ├── usePreloadImages.ts │ │ │ ├── usePrintStyles.ts │ │ │ ├── useSlideBounds.ts │ │ │ ├── useSlideInfo.ts │ │ │ ├── useSwipeControls.ts │ │ │ ├── useTimer.ts │ │ │ ├── useTocTree.ts │ │ │ ├── useViewTransition.ts │ │ │ └── useWakeLock.ts │ │ ├── constants.ts │ │ ├── context.ts │ │ ├── env.ts │ │ ├── index.html │ │ ├── index.ts │ │ ├── internals/ │ │ │ ├── Badge.vue │ │ │ ├── ClicksSlider.vue │ │ │ ├── CodeRunner.vue │ │ │ ├── ContextMenu.vue │ │ │ ├── Controls.vue │ │ │ ├── CurrentProgressBar.vue │ │ │ ├── DevicesSelectors.vue │ │ │ ├── DomElement.vue │ │ │ ├── DragControl.vue │ │ │ ├── Draggable.vue │ │ │ ├── DrawingControls.vue │ │ │ ├── DrawingLayer.vue │ │ │ ├── DrawingPreview.vue │ │ │ ├── ExportPdfTip.vue │ │ │ ├── FormCheckbox.vue │ │ │ ├── FormItem.vue │ │ │ ├── FormSlider.vue │ │ │ ├── Goto.vue │ │ │ ├── IconButton.vue │ │ │ ├── InfoDialog.vue │ │ │ ├── MenuButton.vue │ │ │ ├── Modal.vue │ │ │ ├── NavControls.vue │ │ │ ├── NoteDisplay.vue │ │ │ ├── NoteEditable.vue │ │ │ ├── NoteStatic.vue │ │ │ ├── PresenterMouse.vue │ │ │ ├── PrintContainer.vue │ │ │ ├── PrintSlide.vue │ │ │ ├── PrintSlideClick.vue │ │ │ ├── QuickOverview.vue │ │ │ ├── README.md │ │ │ ├── RecordingControls.vue │ │ │ ├── RecordingDialog.vue │ │ │ ├── ScreenCaptureMirror.vue │ │ │ ├── SegmentControl.vue │ │ │ ├── SelectList.vue │ │ │ ├── Settings.vue │ │ │ ├── ShadowRoot.vue │ │ │ ├── ShikiEditor.vue │ │ │ ├── SideEditor.vue │ │ │ ├── SlideContainer.vue │ │ │ ├── SlideLoading.vue │ │ │ ├── SlideWrapper.vue │ │ │ ├── SlidesShow.vue │ │ │ ├── SyncControls.vue │ │ │ ├── TimerBar.vue │ │ │ ├── TimerInlined.vue │ │ │ ├── TitleIcon.vue │ │ │ ├── VerticalDivider.vue │ │ │ ├── WebCamera.vue │ │ │ └── types.ts │ │ ├── layoutHelper.ts │ │ ├── layouts/ │ │ │ ├── 404.vue │ │ │ ├── center.vue │ │ │ ├── cover.vue │ │ │ ├── default.vue │ │ │ ├── end.vue │ │ │ ├── error.vue │ │ │ ├── fact.vue │ │ │ ├── full.vue │ │ │ ├── iframe-left.vue │ │ │ ├── iframe-right.vue │ │ │ ├── iframe.vue │ │ │ ├── image-left.vue │ │ │ ├── image-right.vue │ │ │ ├── image.vue │ │ │ ├── intro.vue │ │ │ ├── none.vue │ │ │ ├── quote.vue │ │ │ ├── section.vue │ │ │ ├── statement.vue │ │ │ ├── two-cols-header.vue │ │ │ └── two-cols.vue │ │ ├── logic/ │ │ │ ├── color.ts │ │ │ ├── contextMenu.ts │ │ │ ├── dark.ts │ │ │ ├── overview.ts │ │ │ ├── recording.ts │ │ │ ├── route.ts │ │ │ ├── screenshot.ts │ │ │ ├── shortcuts.ts │ │ │ ├── slides.ts │ │ │ ├── snapshot.ts │ │ │ ├── transition.ts │ │ │ └── utils.ts │ │ ├── main.ts │ │ ├── modules/ │ │ │ ├── context.ts │ │ │ ├── mermaid.ts │ │ │ ├── v-click.ts │ │ │ ├── v-drag.ts │ │ │ ├── v-mark.ts │ │ │ └── v-motion.ts │ │ ├── package.json │ │ ├── pages/ │ │ │ ├── 404.vue │ │ │ ├── entry.vue │ │ │ ├── export.vue │ │ │ ├── notes-edit.vue │ │ │ ├── notes.vue │ │ │ ├── overview.vue │ │ │ ├── play.vue │ │ │ ├── presenter/ │ │ │ │ └── print.vue │ │ │ ├── presenter.vue │ │ │ └── print.vue │ │ ├── scripts/ │ │ │ └── unocss-scan.ts │ │ ├── setup/ │ │ │ ├── code-runners.ts │ │ │ ├── context-menu.ts │ │ │ ├── main.ts │ │ │ ├── mermaid.ts │ │ │ ├── monaco.ts │ │ │ ├── root.ts │ │ │ ├── routes.ts │ │ │ ├── shiki-options.ts │ │ │ ├── shiki.ts │ │ │ └── shortcuts.ts │ │ ├── shim-vue.d.ts │ │ ├── shim.d.ts │ │ ├── state/ │ │ │ ├── drawings.ts │ │ │ ├── index.ts │ │ │ ├── shared.ts │ │ │ ├── snapshot.ts │ │ │ ├── storage.ts │ │ │ └── syncState.ts │ │ ├── styles/ │ │ │ ├── code.css │ │ │ ├── index.css │ │ │ ├── katex.css │ │ │ ├── layouts-base.css │ │ │ ├── shiki-twoslash.css │ │ │ ├── transitions.css │ │ │ └── vars.css │ │ ├── uno.config.ts │ │ └── utils.ts │ ├── create-app/ │ │ ├── README.md │ │ ├── build.mjs │ │ ├── index.mjs │ │ ├── package.json │ │ └── template/ │ │ ├── README.md │ │ ├── _gitignore │ │ ├── _npmrc │ │ ├── components/ │ │ │ └── Counter.vue │ │ ├── netlify.toml │ │ ├── package.json │ │ └── vercel.json │ ├── create-theme/ │ │ ├── README.md │ │ ├── index.mjs │ │ ├── package.json │ │ └── template/ │ │ ├── .vscode/ │ │ │ └── extensions.json │ │ ├── README.md │ │ ├── _gitignore │ │ ├── _npmrc │ │ ├── components/ │ │ │ └── .gitkeep │ │ ├── example.md │ │ ├── layouts/ │ │ │ ├── cover.vue │ │ │ └── intro.vue │ │ ├── package.json │ │ ├── setup/ │ │ │ └── shiki.ts │ │ └── styles/ │ │ ├── index.ts │ │ └── layout.css │ ├── parser/ │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── config.ts │ │ │ ├── core.ts │ │ │ ├── fs.ts │ │ │ ├── index.ts │ │ │ ├── timesplit/ │ │ │ │ ├── index.ts │ │ │ │ ├── timesplit.test.ts │ │ │ │ ├── timesplit.ts │ │ │ │ ├── timestring.test.ts │ │ │ │ └── timestring.ts │ │ │ └── utils.ts │ │ └── tsdown.config.ts │ ├── slidev/ │ │ ├── LICENSE │ │ ├── bin/ │ │ │ └── slidev.mjs │ │ ├── node/ │ │ │ ├── cli.ts │ │ │ ├── commands/ │ │ │ │ ├── build.ts │ │ │ │ ├── export.ts │ │ │ │ ├── serve.ts │ │ │ │ └── shared.ts │ │ │ ├── index.ts │ │ │ ├── integrations/ │ │ │ │ ├── addons.ts │ │ │ │ ├── drawings.ts │ │ │ │ ├── snapshots.ts │ │ │ │ └── themes.ts │ │ │ ├── options.ts │ │ │ ├── parser.ts │ │ │ ├── resolver.test.ts │ │ │ ├── resolver.ts │ │ │ ├── setups/ │ │ │ │ ├── indexHtml.ts │ │ │ │ ├── katex.ts │ │ │ │ ├── load.ts │ │ │ │ ├── preparser.ts │ │ │ │ ├── shiki.ts │ │ │ │ ├── transformers.ts │ │ │ │ ├── unocss.ts │ │ │ │ └── vite-plugins.ts │ │ │ ├── syntax/ │ │ │ │ ├── markdown-it/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── markdown-it-escape-code.ts │ │ │ │ │ ├── markdown-it-katex.ts │ │ │ │ │ ├── markdown-it-link.ts │ │ │ │ │ ├── markdown-it-shiki.ts │ │ │ │ │ └── markdown-it-v-drag.ts │ │ │ │ └── transform/ │ │ │ │ ├── code-wrapper.ts │ │ │ │ ├── in-page-css.ts │ │ │ │ ├── index.ts │ │ │ │ ├── katex-wrapper.ts │ │ │ │ ├── magic-move.ts │ │ │ │ ├── mermaid.ts │ │ │ │ ├── monaco.ts │ │ │ │ ├── plant-uml.ts │ │ │ │ ├── slot-sugar.ts │ │ │ │ ├── snippet.ts │ │ │ │ └── utils.ts │ │ │ ├── utils.ts │ │ │ ├── virtual/ │ │ │ │ ├── configs.ts │ │ │ │ ├── deprecated.ts │ │ │ │ ├── global-layers.ts │ │ │ │ ├── index.ts │ │ │ │ ├── layouts.ts │ │ │ │ ├── monaco-deps.ts │ │ │ │ ├── monaco-types.ts │ │ │ │ ├── nav-controls.ts │ │ │ │ ├── setups.ts │ │ │ │ ├── slides.ts │ │ │ │ ├── styles.ts │ │ │ │ ├── titles.ts │ │ │ │ └── types.ts │ │ │ └── vite/ │ │ │ ├── common.ts │ │ │ ├── compilerFlagsVue.ts │ │ │ ├── components.ts │ │ │ ├── contextInjection.ts │ │ │ ├── extendConfig.ts │ │ │ ├── hmrPatch.ts │ │ │ ├── icons.ts │ │ │ ├── index.ts │ │ │ ├── inspect.ts │ │ │ ├── layoutWrapper.ts │ │ │ ├── loaders.ts │ │ │ ├── markdown.ts │ │ │ ├── monacoTypes.ts │ │ │ ├── monacoWrite.ts │ │ │ ├── patchMonacoSourceMap.ts │ │ │ ├── remoteAssets.ts │ │ │ ├── serverRef.ts │ │ │ ├── staticCopy.ts │ │ │ ├── unocss.ts │ │ │ └── vue.ts │ │ ├── package.json │ │ ├── template.md │ │ ├── tsconfig.json │ │ └── tsdown.config.ts │ ├── types/ │ │ ├── README.md │ │ ├── client.d.ts │ │ ├── index.d.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── builtin-layouts.ts │ │ │ ├── cli.ts │ │ │ ├── clicks.ts │ │ │ ├── code-runner.ts │ │ │ ├── config.ts │ │ │ ├── context-menu.ts │ │ │ ├── env.ts │ │ │ ├── frontmatter.ts │ │ │ ├── hmr.ts │ │ │ ├── index.ts │ │ │ ├── options.ts │ │ │ ├── setups.ts │ │ │ ├── toc.ts │ │ │ ├── transform.ts │ │ │ ├── types.ts │ │ │ └── vite.ts │ │ └── tsdown.config.ts │ └── vscode/ │ ├── .vscodeignore │ ├── LICENSE │ ├── README.md │ ├── language-server/ │ │ ├── bin.ts │ │ ├── index.ts │ │ ├── languagePlugin.ts │ │ ├── prettierService.ts │ │ ├── protocol.ts │ │ └── volar-service-yaml.ts │ ├── package.json │ ├── schema/ │ │ ├── frontmatter.json │ │ └── headmatter.json │ ├── scripts/ │ │ ├── publish.ts │ │ └── schema.ts │ ├── src/ │ │ ├── commands.ts │ │ ├── composables/ │ │ │ ├── useDebouncedComputed.ts │ │ │ ├── useDevServer.ts │ │ │ ├── useFocusedSlide.ts │ │ │ ├── useProjectFromDoc.ts │ │ │ └── useServerDetector.ts │ │ ├── configs.ts │ │ ├── html/ │ │ │ ├── error.ts │ │ │ └── ready.ts │ │ ├── index.ts │ │ ├── languageClient.ts │ │ ├── lmTools.ts │ │ ├── projects.ts │ │ ├── utils/ │ │ │ ├── findPossibleEntries.ts │ │ │ ├── findShallowestPath.ts │ │ │ ├── getFirstDisplayedChild.ts │ │ │ ├── getSlidesTitle.ts │ │ │ └── toRelativePath.ts │ │ └── views/ │ │ ├── annotations.ts │ │ ├── foldings.ts │ │ ├── logger.ts │ │ ├── previewWebview.ts │ │ ├── projectsTree.ts │ │ └── slidesTree.ts │ ├── syntaxes/ │ │ ├── .vscode/ │ │ │ └── settings.json │ │ ├── codeblock-patch.ts │ │ ├── codeblock.json │ │ ├── language-configuration.json │ │ ├── markdown.json │ │ ├── pages.md │ │ ├── slidev.example.md │ │ ├── slidev.tmLanguage.json │ │ └── tsconfig.json │ ├── tsconfig.json │ └── tsdown.config.ts ├── patches/ │ └── @hedgedoc__markdown-it-plugins@2.1.4.patch ├── pnpm-workspace.yaml ├── scripts/ │ ├── demo.mjs │ ├── gen-layouts.ts │ ├── pack.mjs │ ├── publish.mjs │ ├── remove-overridden-deps.mjs │ └── update-versions.mjs ├── shim.d.ts ├── skills/ │ ├── GENERATION.md │ └── slidev/ │ ├── README.md │ ├── SKILL.md │ └── references/ │ ├── animation-click-marker.md │ ├── animation-drawing.md │ ├── animation-rough-marker.md │ ├── api-slide-hooks.md │ ├── build-og-image.md │ ├── build-pdf.md │ ├── build-remote-assets.md │ ├── build-seo-meta.md │ ├── code-groups.md │ ├── code-import-snippet.md │ ├── code-line-highlighting.md │ ├── code-line-numbers.md │ ├── code-magic-move.md │ ├── code-max-height.md │ ├── code-twoslash.md │ ├── core-animations.md │ ├── core-cli.md │ ├── core-components.md │ ├── core-exporting.md │ ├── core-frontmatter.md │ ├── core-global-context.md │ ├── core-headmatter.md │ ├── core-hosting.md │ ├── core-layouts.md │ ├── core-syntax.md │ ├── diagram-latex.md │ ├── diagram-mermaid.md │ ├── diagram-plantuml.md │ ├── editor-monaco-run.md │ ├── editor-monaco-write.md │ ├── editor-monaco.md │ ├── editor-prettier.md │ ├── editor-side.md │ ├── editor-vscode.md │ ├── layout-canvas-size.md │ ├── layout-draggable.md │ ├── layout-global-layers.md │ ├── layout-slots.md │ ├── layout-transform.md │ ├── layout-zoom.md │ ├── presenter-notes-ruby.md │ ├── presenter-recording.md │ ├── presenter-remote.md │ ├── presenter-timer.md │ ├── style-direction.md │ ├── style-icons.md │ ├── style-scoped.md │ ├── syntax-block-frontmatter.md │ ├── syntax-frontmatter-merging.md │ ├── syntax-importing-slides.md │ ├── syntax-mdc.md │ └── tool-eject-theme.md ├── taze.config.ts ├── test/ │ ├── __snapshots__/ │ │ ├── parser.test.ts.snap │ │ ├── transform-all.test.ts.snap │ │ ├── transform.test.ts.snap │ │ └── utils.test.ts.snap │ ├── _tutils.ts │ ├── fixtures/ │ │ ├── markdown/ │ │ │ ├── frontmatter.md │ │ │ ├── mdc.md │ │ │ ├── minimal.md │ │ │ ├── multi-entries.md │ │ │ └── sub/ │ │ │ ├── nested1-4.md │ │ │ ├── page1.md │ │ │ ├── page2.md │ │ │ └── pages3-4.md │ │ └── snippets/ │ │ └── snippet.ts │ ├── mermaid-renderer.test.ts │ ├── parser.test.ts │ ├── transform-all.test.ts │ ├── transform-magic-move.test.ts │ ├── transform.test.ts │ └── utils.test.ts ├── tsconfig.json ├── tsdown.config.ts └── vitest.config.ts
SYMBOL INDEX (527 symbols across 198 files)
FILE: cypress/e2e/examples/basic.spec.ts
type Chainable (line 5) | interface Chainable<Subject> {
constant BASE (line 15) | const BASE = 'http://localhost:3041'
function goPage (line 22) | function goPage(no: number) {
FILE: cypress/e2e/examples/presenter-resizer.spec.ts
constant LAYOUT_KEY (line 1) | const LAYOUT_KEY = 'slidev-presenter-layout'
function visitPresenter (line 3) | function visitPresenter(layout: 1 | 2 | 3) {
FILE: cypress/e2e/examples/smoke.spec.ts
function testAllSlides (line 2) | async function testAllSlides() {
FILE: demo/starter/snippets/external.ts
function emptyArray (line 5) | function emptyArray<T>(length: number) {
function sayHello (line 10) | function sayHello() {
FILE: demo/vue-runner/setup/code-runners.ts
method vue (line 7) | async vue(code) {
FILE: docs/.vitepress/addons.ts
type AddonInfo (line 3) | type AddonInfo = Omit<ThemeInfo, 'previews'>
FILE: docs/.vitepress/config.ts
method shikiSetup (line 55) | async shikiSetup(shiki) {
method config (line 77) | config(md) {
FILE: docs/.vitepress/showcases.ts
type ShowCaseInfo (line 1) | interface ShowCaseInfo {
FILE: docs/.vitepress/sidebar-gen.ts
type ParsedFile (line 9) | interface ParsedFile {
function parseFile (line 16) | function parseFile(file: string) {
function getSidebarObject (line 29) | async function getSidebarObject() {
FILE: docs/.vitepress/theme/index.ts
method enhanceApp (line 15) | enhanceApp({ app }: EnhanceAppContext) {
FILE: docs/.vitepress/themes.ts
type ThemeInfo (line 1) | interface ThemeInfo {
FILE: docs/.vitepress/utils.ts
function removeHash (line 4) | function removeHash(link: string) {
function getGuideTitle (line 9) | function getGuideTitle(id: string) {
function resolveLink (line 13) | function resolveLink(link: string): {
FILE: docs/features/index.data.ts
type Feature (line 4) | interface Feature {
method transform (line 18) | transform(data) {
FILE: docs/vite.config.ts
constant IS_ROOT_ENGLISH_DOC (line 12) | const IS_ROOT_ENGLISH_DOC = config.locales?.root.label.includes('English...
FILE: packages/client/builtin/VAfter.ts
method render (line 12) | render() {
FILE: packages/client/builtin/VClick.ts
method render (line 31) | render() {
FILE: packages/client/builtin/VClicks.ts
method render (line 42) | render() {
FILE: packages/client/builtin/VSwitch.ts
method setup (line 36) | setup({ at, unmount, transition, tag, childTag }, { slots }) {
FILE: packages/client/composables/useClicks.ts
function normalizeSingleAtValue (line 6) | function normalizeSingleAtValue(at: RawSingleAtValue): NormalizedSingleC...
function normalizeRangeAtValue (line 25) | function normalizeRangeAtValue(at: RawAtValue): NormalizedRangeClickValue {
function createClicksContextBase (line 31) | function createClicksContextBase(
function createFixedClicks (line 166) | function createFixedClicks(
FILE: packages/client/composables/useDarkMode.ts
function useDarkMode (line 3) | function useDarkMode() {
FILE: packages/client/composables/useDragElements.ts
type DragElementDataSource (line 14) | type DragElementDataSource = 'frontmatter' | 'prop' | 'directive'
type DragElementMarkdownSource (line 18) | type DragElementMarkdownSource = [startLine: number, endLine: number, in...
type DragElementsUpdater (line 20) | type DragElementsUpdater = (id: string, posStr: string, type: DragElemen...
function useDragElementsUpdater (line 24) | function useDragElementsUpdater(no: number) {
function useDragElement (line 117) | function useDragElement(directive: DirectiveBinding | null, posRaw?: str...
type DragElementState (line 300) | type DragElementState = ReturnType<typeof useDragElement>
FILE: packages/client/composables/useDrawings.ts
method get (line 41) | get() {
method set (line 44) | set(v: DrawingMode | 'arrow') {
function clearDrauu (line 67) | function clearDrauu() {
function updateState (line 73) | function updateState() {
function loadCanvas (line 79) | function loadCanvas(page?: number) {
FILE: packages/client/composables/useEmbeddedCtrl.ts
function useEmbeddedControl (line 5) | function useEmbeddedControl() {
FILE: packages/client/composables/useHideCursorIdle.ts
constant TIMEOUT (line 6) | const TIMEOUT = 2000
function useHideCursorIdle (line 8) | function useHideCursorIdle(
FILE: packages/client/composables/useIME.ts
function useIME (line 4) | function useIME(content: ModelRef<string>) {
FILE: packages/client/composables/useNav.ts
type SlidevContextNav (line 19) | interface SlidevContextNav {
type SlidevContextNavState (line 70) | interface SlidevContextNavState {
type SlidevContextNavFull (line 90) | interface SlidevContextNavFull extends SlidevContextNav, SlidevContextNa...
function useNavBase (line 92) | function useNavBase(
function useFixedNav (line 251) | function useFixedNav(
method get (line 300) | get() {
method set (line 306) | set(v) {
function getPrimaryClicks (line 312) | function getPrimaryClicks(
FILE: packages/client/composables/usePreloadImages.ts
function resolveUrl (line 9) | function resolveUrl(url: string): string {
function preloadImage (line 16) | function preloadImage(url: string): void {
function preloadSlideImages (line 32) | function preloadSlideImages(route: SlideRoute): void {
function usePreloadImages (line 40) | function usePreloadImages(
FILE: packages/client/composables/usePrintStyles.ts
function usePrintStyles (line 6) | function usePrintStyles() {
function patchMonacoColors (line 24) | function patchMonacoColors() {
FILE: packages/client/composables/useSlideBounds.ts
function useSlideBounds (line 6) | function useSlideBounds(slideElement = inject(injectionSlideElement, ref...
FILE: packages/client/composables/useSlideInfo.ts
type UseSlideInfo (line 8) | interface UseSlideInfo {
function useSlideInfo (line 13) | function useSlideInfo(no: number): UseSlideInfo {
function useDynamicSlideInfo (line 58) | function useDynamicSlideInfo(no: MaybeRef<number>) {
FILE: packages/client/composables/useSwipeControls.ts
function useSwipeControls (line 7) | function useSwipeControls(root: Ref<HTMLElement | undefined>) {
FILE: packages/client/composables/useTimer.ts
function useTimer (line 7) | function useTimer() {
FILE: packages/client/composables/useTocTree.ts
function addToTree (line 6) | function addToTree(tree: TocItem[], route: SlideRoute, level = 1) {
function getTreeWithActiveStatuses (line 24) | function getTreeWithActiveStatuses(
function filterTree (line 52) | function filterTree(tree: TocItem[], level = 1): TocItem[] {
function useTocTree (line 61) | function useTocTree(
FILE: packages/client/composables/useViewTransition.ts
function useViewTransition (line 6) | function useViewTransition() {
FILE: packages/client/composables/useWakeLock.ts
function useWakeLock (line 5) | function useWakeLock() {
FILE: packages/client/constants.ts
constant CLASS_VCLICK_TARGET (line 17) | const CLASS_VCLICK_TARGET = 'slidev-vclick-target'
constant CLASS_VCLICK_HIDDEN (line 18) | const CLASS_VCLICK_HIDDEN = 'slidev-vclick-hidden'
constant CLASS_VCLICK_FADE (line 19) | const CLASS_VCLICK_FADE = 'slidev-vclick-fade'
constant CLASS_VCLICK_GONE (line 20) | const CLASS_VCLICK_GONE = 'slidev-vclick-gone'
constant CLASS_VCLICK_HIDDEN_EXP (line 21) | const CLASS_VCLICK_HIDDEN_EXP = 'slidev-vclick-hidden-explicitly'
constant CLASS_VCLICK_CURRENT (line 22) | const CLASS_VCLICK_CURRENT = 'slidev-vclick-current'
constant CLASS_VCLICK_PRIOR (line 23) | const CLASS_VCLICK_PRIOR = 'slidev-vclick-prior'
constant CLASS_VCLICK_DISPLAY_NONE (line 24) | const CLASS_VCLICK_DISPLAY_NONE = 'slidev-vclick-display-none'
constant CLICKS_MAX (line 26) | const CLICKS_MAX = 999999
constant TRUST_ORIGINS (line 28) | const TRUST_ORIGINS = [
constant FRONTMATTER_FIELDS (line 33) | const FRONTMATTER_FIELDS = [
constant HEADMATTER_FIELDS (line 51) | const HEADMATTER_FIELDS = [
FILE: packages/client/context.ts
function useSlideContext (line 19) | function useSlideContext() {
type SlideContext (line 45) | type SlideContext = ReturnType<typeof useSlideContext>
function frontmatterToProps (line 53) | function frontmatterToProps(frontmatter: Record<string, any>, pageNo: nu...
FILE: packages/client/internals/types.ts
type SelectionItem (line 1) | interface SelectionItem<T> {
FILE: packages/client/layoutHelper.ts
function resolveAssetUrl (line 6) | function resolveAssetUrl(url: string) {
function handleBackground (line 12) | function handleBackground(background?: string, dim = false, backgroundSi...
FILE: packages/client/logic/color.ts
function getHashColorFromString (line 25) | function getHashColorFromString(
function getHsla (line 39) | function getHsla(
function getPluginColor (line 50) | function getPluginColor(name: string, opacity = 1): string {
FILE: packages/client/logic/contextMenu.ts
function openContextMenu (line 14) | function openContextMenu(x: number, y: number) {
function closeContextMenu (line 22) | function closeContextMenu() {
function onContextMenu (line 26) | function onContextMenu(ev: MouseEvent) {
FILE: packages/client/logic/dark.ts
method get (line 10) | get() {
method set (line 17) | set(v) {
FILE: packages/client/logic/overview.ts
function prevOverviewPage (line 11) | function prevOverviewPage() {
function nextOverviewPage (line 16) | function nextOverviewPage() {
function upOverviewPage (line 21) | function upOverviewPage() {
function downOverviewPage (line 31) | function downOverviewPage() {
FILE: packages/client/logic/recording.ts
type Defined (line 9) | type Defined<T> = T extends undefined ? never : T
type MimeType (line 10) | type MimeType = Defined<RecorderOptions['mimeType']>
function getFilename (line 25) | function getFilename(media?: string, mimeType?: string) {
function getSupportedMimeTypes (line 37) | function getSupportedMimeTypes() {
method onUpdated (line 51) | onUpdated() {
function download (line 63) | function download(name: string, url: string) {
function useRecording (line 72) | function useRecording() {
FILE: packages/client/logic/route.ts
function useRouteQuery (line 5) | function useRouteQuery<T extends string | string[]>(
FILE: packages/client/logic/screenshot.ts
function startScreenshotSession (line 3) | async function startScreenshotSession(width: number, height: number) {
type ScreenshotSession (line 58) | type ScreenshotSession = Awaited<ReturnType<typeof startScreenshotSession>>
FILE: packages/client/logic/shortcuts.ts
function registerShortcuts (line 11) | function registerShortcuts() {
FILE: packages/client/logic/slides.ts
function getSlide (line 9) | function getSlide(no: number | string) {
function getSlidePath (line 15) | function getSlidePath(
function useIsSlideActive (line 26) | function useIsSlideActive() {
function onSlideEnter (line 32) | function onSlideEnter(cb: () => void) {
function onSlideLeave (line 37) | function onSlideLeave(cb: () => void) {
FILE: packages/client/logic/snapshot.ts
class SlideSnapshotManager (line 16) | class SlideSnapshotManager {
method getSnapshot (line 19) | getSnapshot(slideNo: number, isDark: boolean) {
method saveSnapshot (line 34) | private async saveSnapshot(slideNo: number, dataUrl: string, isDark: b...
method startCapturing (line 49) | async startCapturing(nav: SlidevContextNavFull) {
FILE: packages/client/logic/transition.ts
function resolveTransition (line 12) | function resolveTransition(transition?: string | TransitionGroupProps, i...
function getCurrentTransition (line 42) | function getCurrentTransition(direction: number, currentRoute?: SlideRou...
FILE: packages/client/logic/utils.ts
function makeId (line 3) | function makeId(length = 5) {
function updateCodeHighlightRange (line 12) | function updateCodeHighlightRange(
FILE: packages/client/main.ts
function main (line 7) | async function main() {
FILE: packages/client/modules/context.ts
type SlidevContext (line 5) | interface SlidevContext {
FILE: packages/client/modules/mermaid.ts
function renderMermaid (line 14) | async function renderMermaid(lzEncoded: string, options: any) {
FILE: packages/client/modules/v-click.ts
function createVClickDirectives (line 15) | function createVClickDirectives() {
function resolveClick (line 119) | function resolveClick(el: Element | string, dir: DirectiveBinding<any>, ...
function unmounted (line 155) | function unmounted(el: HTMLElement, dir: DirectiveBinding<any>) {
FILE: packages/client/modules/v-drag.ts
function createVDragDirective (line 6) | function createVDragDirective() {
FILE: packages/client/modules/v-mark.ts
type RoughDirectiveOptions (line 9) | interface RoughDirectiveOptions extends Partial<RoughAnnotationConfig> {
type RoughDirectiveValue (line 13) | type RoughDirectiveValue = RawAtValue | RoughDirectiveOptions
function addClass (line 15) | function addClass(options: RoughDirectiveOptions, cls: string) {
function createVMarkDirective (line 74) | function createVMarkDirective() {
FILE: packages/client/modules/v-motion.ts
function createVMotionDirectives (line 11) | function createVMotionDirectives() {
FILE: packages/client/setup/code-runners.ts
function runJavaScript (line 65) | function runJavaScript(code: string): CodeRunnerOutputs {
function runTypeScript (line 171) | async function runTypeScript(code: string) {
function transformImports (line 193) | function transformImports(context: ts.TransformationContext): ts.Transfo...
FILE: packages/client/setup/main.ts
function setupMain (line 15) | async function setupMain(app: App) {
FILE: packages/client/setup/monaco.ts
method getWorker (line 27) | getWorker(_, label) {
class ContextViewService2 (line 40) | class ContextViewService2 extends ContextViewService {
method showContextView (line 41) | showContextView(...args: any) {
function _addFile (line 137) | async function _addFile(raw: () => Promise<{ default: string }>, path: s...
function addFile (line 146) | async function addFile(raw: () => Promise<{ default: string }>, path: st...
FILE: packages/client/setup/root.ts
function setupRoot (line 17) | function setupRoot() {
FILE: packages/client/setup/routes.ts
function setupRoutes (line 5) | function setupRoutes() {
FILE: packages/client/setup/shiki-options.ts
method loadTheme (line 11) | loadTheme() {
function resolveShikiOptions (line 16) | function resolveShikiOptions(options: (ShikiSetupReturn | void)[]) {
function extractThemeName (line 106) | function extractThemeName(theme?: ThemeRegistrationAny | string): string...
function extractThemeNames (line 116) | function extractThemeNames(themes?: Record<string, ThemeRegistrationAny ...
FILE: packages/client/setup/shortcuts.ts
function setupShortcuts (line 11) | function setupShortcuts() {
FILE: packages/client/shim-vue.d.ts
type SlideContext (line 2) | type SlideContext = import('./context').SlideContext
type ComponentCustomProperties (line 3) | interface ComponentCustomProperties extends SlideContext {
type RouteMeta (line 10) | interface RouteMeta {
FILE: packages/client/state/drawings.ts
type DrawingsState (line 5) | type DrawingsState = Record<number, string | undefined>
FILE: packages/client/state/shared.ts
type SharedState (line 5) | interface SharedState {
FILE: packages/client/state/snapshot.ts
type SnapshotState (line 5) | type SnapshotState = Record<string, {
FILE: packages/client/state/storage.ts
function lockShortcuts (line 28) | function lockShortcuts() {
function togglePresenterLayout (line 86) | function togglePresenterLayout() {
function increasePresenterFontSize (line 92) | function increasePresenterFontSize() {
function decreasePresenterFontSize (line 96) | function decreasePresenterFontSize() {
FILE: packages/client/state/syncState.ts
type SyncWrite (line 3) | type SyncWrite<State extends object> = (state: State, updating?: boolean...
type Sync (line 5) | interface Sync {
type BuiltinSync (line 10) | interface BuiltinSync extends Sync {
method init (line 19) | init<State extends object>(channelKey: string, onUpdate: (data: Partial<...
method disable (line 45) | disable() {
function disableBuiltinSync (line 57) | function disableBuiltinSync() {
function addSyncMethod (line 61) | function addSyncMethod(sync: Sync) {
function createSyncState (line 71) | function createSyncState<State extends object>(serverState: State, defau...
FILE: packages/client/utils.ts
function getSlideClass (line 5) | function getSlideClass(route?: SlideRoute, extra = '') {
function downloadPDF (line 15) | async function downloadPDF() {
function directiveInject (line 27) | function directiveInject<T = unknown>(dir: DirectiveBinding<any>, key: I...
function directiveProvide (line 31) | function directiveProvide<T = unknown>(dir: DirectiveBinding<any>, key: ...
FILE: packages/create-app/build.mjs
function main (line 20) | async function main() {
FILE: packages/create-app/index.mjs
function init (line 26) | async function init() {
function copy (line 142) | function copy(src, dest) {
function getValidPackageName (line 151) | async function getValidPackageName(projectName) {
function copyDir (line 179) | function copyDir(srcDir, destDir) {
function emptyDir (line 188) | function emptyDir(dir) {
FILE: packages/create-theme/index.mjs
function init (line 25) | async function init() {
function prepareTemplate (line 94) | function prepareTemplate(root, templateDir, packageName) {
function copy (line 128) | function copy(src, dest) {
function getValidPackageName (line 137) | function getValidPackageName(projectName) {
function getThemeName (line 144) | function getThemeName(pkgName) {
function copyDir (line 148) | function copyDir(srcDir, destDir) {
function emptyDir (line 157) | function emptyDir(dir) {
FILE: packages/parser/src/config.ts
function getDefaultConfig (line 5) | function getDefaultConfig(): SlidevConfig {
function resolveConfig (line 59) | function resolveConfig(headmatter: any, themeMeta: SlidevThemeMeta = {},...
function verifyConfig (line 112) | function verifyConfig(
function resolveFonts (line 136) | function resolveFonts(fonts: FontOptions = {}): ResolvedFontOptions {
function resolveDrawings (line 213) | function resolveDrawings(options: DrawingsOptions = {}, filepath?: strin...
FILE: packages/parser/src/core.ts
type SlidevParserOptions (line 5) | interface SlidevParserOptions {
function stringify (line 10) | function stringify(data: SlidevMarkdown) {
function stringifySlide (line 14) | function stringifySlide(data: SourceSlideInfo, idx = 0) {
function prettifySlide (line 20) | function prettifySlide(data: SourceSlideInfo) {
function prettify (line 33) | function prettify(data: SlidevMarkdown) {
function matter (line 38) | function matter(code: string, options: SlidevParserOptions) {
constant IMAGE_EXTENSIONS (line 69) | const IMAGE_EXTENSIONS = /\.(?:png|jpe?g|gif|svg|webp|avif|ico|bmp|tiff?...
function extractImagesUsage (line 75) | function extractImagesUsage(content: string, frontmatter: Record<string,...
function detectFeatures (line 123) | function detectFeatures(code: string): SlidevDetectedFeatures {
function parseSlide (line 132) | function parseSlide(raw: string, options: SlidevParserOptions = {}): Omi...
function parse (line 179) | async function parse(
function parseSync (line 276) | function parseSync(
function scanMonacoReferencedMods (line 344) | function scanMonacoReferencedMods(md: string) {
function hash (line 371) | function hash(str: string) {
FILE: packages/parser/src/fs.ts
function injectPreparserExtensionLoader (line 13) | function injectPreparserExtensionLoader(fn: PreparserExtensionLoader) {
type LoadedSlidevData (line 21) | type LoadedSlidevData = Omit<SlidevData, 'config' | 'themeMeta'>
function load (line 23) | async function load(
function save (line 144) | async function save(markdown: SlidevMarkdown) {
FILE: packages/parser/src/timesplit/timesplit.ts
type TimesplitInput (line 3) | interface TimesplitInput {
type TimesplitOutput (line 9) | interface TimesplitOutput {
function parseTimesplits (line 17) | function parseTimesplits(inputs: TimesplitInput[]): TimesplitOutput[] {
FILE: packages/parser/src/timesplit/timestring.ts
function parseTimeString (line 16) | function parseTimeString(timestamp: string | number): {
FILE: packages/parser/src/utils.ts
function parseRangeString (line 8) | function parseRangeString(total: number, rangeStr?: string) {
function parseAspectRatio (line 34) | function parseAspectRatio(str: string | number) {
FILE: packages/slidev/node/cli.ts
constant CONFIG_RESTART_FIELDS (line 25) | const CONFIG_RESTART_FIELDS: (keyof SlidevConfig)[] = [
constant FILES_CHANGE_RESTART (line 36) | const FILES_CHANGE_RESTART = [
function restartServer (line 119) | async function restartServer() {
function initServer (line 129) | async function initServer() {
function openTunnel (line 211) | async function openTunnel(port: number) {
method action (line 224) | action() {
method action (line 231) | action() {
method action (line 238) | action() {
method action (line 245) | action() {
method action (line 257) | async action() {
function bindShortcut (line 273) | function bindShortcut() {
function getViteServerPort (line 572) | function getViteServerPort(server: ViteDevServer): number {
function commonOptions (line 579) | function commonOptions(args: Argv<object>) {
function exportOptions (line 593) | function exportOptions<T>(args: Argv<T>) {
function printInfo (line 652) | function printInfo(
FILE: packages/slidev/node/commands/build.ts
function build (line 12) | async function build(
FILE: packages/slidev/node/commands/export.ts
type ExportOptions (line 16) | interface ExportOptions {
type ExportPngResult (line 43) | interface ExportPngResult {
function addToTree (line 49) | function addToTree(tree: TocItem[], info: SlideInfo, slideIndexes: Recor...
function makeOutline (line 67) | function makeOutline(tree: TocItem[]): string {
type ExportNotesOptions (line 77) | interface ExportNotesOptions {
function createSlidevProgress (line 85) | function createSlidevProgress(indeterminate = false) {
function exportNotes (line 121) | async function exportNotes({
function exportSlides (line 165) | async function exportSlides({
function getExportOptions (line 571) | function getExportOptions(args: ExportArgs, options: ResolvedSlidevOptio...
function importPlaywright (line 623) | async function importPlaywright(): Promise<typeof import('playwright-chr...
FILE: packages/slidev/node/commands/serve.ts
function createServer (line 8) | async function createServer(
FILE: packages/slidev/node/commands/shared.ts
function getSlideTitle (line 13) | function getSlideTitle(data: SlidevData) {
function resolveViteConfigs (line 20) | async function resolveViteConfigs(
FILE: packages/slidev/node/integrations/addons.ts
function resolveAddons (line 7) | async function resolveAddons(addonsInConfig: string[]) {
FILE: packages/slidev/node/integrations/drawings.ts
function resolveDrawingsDir (line 7) | function resolveDrawingsDir(options: ResolvedSlidevOptions): string | un...
function loadDrawings (line 16) | async function loadDrawings(options: ResolvedSlidevOptions) {
function writeDrawings (line 41) | async function writeDrawings(options: ResolvedSlidevOptions, drawing: Re...
FILE: packages/slidev/node/integrations/snapshots.ts
function resolveSnapshotsDir (line 6) | function resolveSnapshotsDir(options: ResolvedSlidevOptions): string {
function loadSnapshots (line 10) | async function loadSnapshots(options: ResolvedSlidevOptions) {
function writeSnapshots (line 19) | async function writeSnapshots(options: ResolvedSlidevOptions, data: Reco...
FILE: packages/slidev/node/integrations/themes.ts
function getThemeMeta (line 20) | async function getThemeMeta(name: string, root: string) {
FILE: packages/slidev/node/options.ts
function resolveOptions (line 17) | async function resolveOptions(
function createDataUtils (line 77) | async function createDataUtils(resolved: Omit<ResolvedSlidevOptions, 'ut...
function getDefine (line 118) | function getDefine(options: Omit<ResolvedSlidevOptions, 'utils'>): Recor...
FILE: packages/slidev/node/resolver.ts
function resolveImportUrl (line 23) | async function resolveImportUrl(id: string) {
function toAtFS (line 27) | function toAtFS(path: string) {
function resolveImportPath (line 36) | async function resolveImportPath(importName: string, ensure = false) {
function findPkgRoot (line 60) | async function findPkgRoot(dep: string, parent: string, ensure = false) {
function findGlobalPkgRoot (line 70) | async function findGlobalPkgRoot(name: string, ensure = false) {
function resolveEntry (line 84) | async function resolveEntry(entryRaw: string) {
function createResolver (line 111) | function createResolver(type: 'theme' | 'addon', officials: Record<strin...
function getUserPkgJson (line 174) | async function getUserPkgJson(userRoot: string) {
function hasWorkspacePackageJSON (line 183) | async function hasWorkspacePackageJSON(root: string): Promise<boolean> {
function hasRootFile (line 191) | function hasRootFile(root: string): boolean {
function searchForWorkspaceRoot (line 213) | async function searchForWorkspaceRoot(
function getRoots (line 232) | async function getRoots(entry?: string): Promise<RootsInfo> {
function resolveSourceFiles (line 255) | function resolveSourceFiles(
FILE: packages/slidev/node/setups/indexHtml.ts
function escapeHtml (line 14) | function escapeHtml(str: string): string {
function toAttrValue (line 23) | function toAttrValue(unsafe: unknown) {
function collectPreloadImages (line 27) | function collectPreloadImages(data: Omit<ResolvedSlidevOptions, 'utils'>...
function setupIndexHtml (line 54) | async function setupIndexHtml({ mode, entry, clientRoot, userRoot, roots...
FILE: packages/slidev/node/setups/katex.ts
function setupKatex (line 5) | async function setupKatex(roots: string[]): Promise<KatexOptions> {
FILE: packages/slidev/node/setups/load.ts
function loadSetups (line 6) | async function loadSetups<F extends (...args: any) => any>(
FILE: packages/slidev/node/setups/preparser.ts
function setupPreparser (line 8) | function setupPreparser() {
FILE: packages/slidev/node/setups/shiki.ts
function setupShiki (line 11) | async function setupShiki(roots: string[]) {
FILE: packages/slidev/node/setups/transformers.ts
function setupTransformers (line 4) | async function setupTransformers(roots: string[]) {
FILE: packages/slidev/node/setups/unocss.ts
function setupUnocss (line 11) | async function setupUnocss(
FILE: packages/slidev/node/setups/vite-plugins.ts
function setupVitePlugins (line 5) | async function setupVitePlugins(options: ResolvedSlidevOptions): Promise...
FILE: packages/slidev/node/syntax/markdown-it/index.ts
function useMarkdownItPlugins (line 14) | async function useMarkdownItPlugins(md: MarkdownExit, options: ResolvedS...
FILE: packages/slidev/node/syntax/markdown-it/markdown-it-escape-code.ts
function MarkdownItEscapeInlineCode (line 3) | function MarkdownItEscapeInlineCode(md: MarkdownExit) {
FILE: packages/slidev/node/syntax/markdown-it/markdown-it-katex.ts
function isValidDelim (line 18) | function isValidDelim(state: any, pos: number) {
function math_inline (line 40) | function math_inline(state: any, silent: boolean) {
function math_block (line 108) | function math_block(state: any, start: number, end: number, silent: bool...
function MarkdownItKatex (line 166) | function MarkdownItKatex(md: any, options: KatexOptions) {
FILE: packages/slidev/node/syntax/markdown-it/markdown-it-link.ts
function MarkdownItLink (line 3) | function MarkdownItLink(md: MarkdownExit) {
FILE: packages/slidev/node/syntax/markdown-it/markdown-it-shiki.ts
function MarkdownItShiki (line 7) | async function MarkdownItShiki({ data: { config }, mode, utils: { shiki,...
FILE: packages/slidev/node/syntax/markdown-it/markdown-it-v-drag.ts
type Token (line 5) | type Token = ReturnType<MarkdownExit['parseInline']>[number]
function MarkdownItVDrag (line 10) | function MarkdownItVDrag(md: MarkdownExit, markdownTransformMap: Map<str...
FILE: packages/slidev/node/syntax/transform/code-wrapper.ts
function transformCodeWrapper (line 10) | function transformCodeWrapper(ctx: MarkdownTransformContext) {
FILE: packages/slidev/node/syntax/transform/in-page-css.ts
function transformPageCSS (line 7) | function transformPageCSS(ctx: MarkdownTransformContext) {
FILE: packages/slidev/node/syntax/transform/index.ts
function getMarkdownTransformers (line 13) | async function getMarkdownTransformers(options: ResolvedSlidevOptions): ...
FILE: packages/slidev/node/syntax/transform/katex-wrapper.ts
function transformKaTexWrapper (line 6) | function transformKaTexWrapper(ctx: MarkdownTransformContext) {
FILE: packages/slidev/node/syntax/transform/magic-move.ts
function parseLineNumbersOption (line 10) | function parseLineNumbersOption(options: string) {
function transformMagicMove (line 17) | async function transformMagicMove(ctx: MarkdownTransformContext) {
FILE: packages/slidev/node/syntax/transform/mermaid.ts
function transformMermaid (line 7) | function transformMermaid(ctx: MarkdownTransformContext) {
FILE: packages/slidev/node/syntax/transform/monaco.ts
function transformMonaco (line 4) | function transformMonaco(ctx: MarkdownTransformContext) {
FILE: packages/slidev/node/syntax/transform/plant-uml.ts
function transformPlantUml (line 4) | function transformPlantUml(ctx: MarkdownTransformContext) {
FILE: packages/slidev/node/syntax/transform/slot-sugar.ts
function transformSlotSugar (line 4) | function transformSlotSugar(
FILE: packages/slidev/node/syntax/transform/snippet.ts
function dedent (line 10) | function dedent(text: string): string {
function findRegion (line 64) | function findRegion(lines: Array<string>, regionName: string) {
function transformSnippet (line 117) | function transformSnippet({ s, slide, options }: MarkdownTransformContex...
FILE: packages/slidev/node/syntax/transform/utils.ts
function normalizeRangeStr (line 1) | function normalizeRangeStr(rangeStr = '') {
function getCodeBlocks (line 5) | function getCodeBlocks(md: string) {
function getCommentBlocks (line 28) | function getCommentBlocks(md: string) {
function escapeVueInCode (line 53) | function escapeVueInCode(md: string) {
FILE: packages/slidev/node/utils.ts
type Token (line 10) | type Token = ReturnType<MarkdownExit['parseInline']>[number]
type Jiti (line 12) | type Jiti = ReturnType<typeof createJiti>
function loadModule (line 14) | function loadModule<T = unknown>(absolutePath: string): Promise<T> {
function stringifyMarkdownTokens (line 22) | function stringifyMarkdownTokens(tokens: Token[]) {
function generateFontParams (line 31) | function generateFontParams(options: ResolvedFontOptions) {
function generateGoogleFontsUrl (line 42) | function generateGoogleFontsUrl(options: ResolvedFontOptions) {
function generateCoollabsFontsUrl (line 46) | function generateCoollabsFontsUrl(options: ResolvedFontOptions) {
function updateFrontmatterPatch (line 53) | function updateFrontmatterPatch(source: SourceSlideInfo, frontmatter: Re...
function getBodyJson (line 87) | function getBodyJson(req: Connect.IncomingMessage) {
function makeAbsoluteImportGlob (line 103) | function makeAbsoluteImportGlob(
FILE: packages/slidev/node/virtual/configs.ts
method getContent (line 7) | getContent({ data, remote }) {
FILE: packages/slidev/node/virtual/deprecated.ts
method getContent (line 10) | getContent() {
method getContent (line 25) | getContent() {
FILE: packages/slidev/node/virtual/global-layers.ts
method getContent (line 7) | getContent({ userRoot, roots }) {
FILE: packages/slidev/node/virtual/layouts.ts
method getContent (line 7) | async getContent({ utils }) {
FILE: packages/slidev/node/virtual/monaco-deps.ts
method getContent (line 7) | async getContent({ userRoot, data }) {
FILE: packages/slidev/node/virtual/monaco-types.ts
function mapModuleNameToModule (line 26) | function mapModuleNameToModule(moduleSpecifier: string) {
FILE: packages/slidev/node/virtual/nav-controls.ts
method getContent (line 8) | getContent({ roots }) {
FILE: packages/slidev/node/virtual/setups.ts
function createSetupTemplate (line 5) | function createSetupTemplate(name: string): VirtualModuleTemplate {
FILE: packages/slidev/node/virtual/slides.ts
constant VIRTUAL_SLIDE_PREFIX (line 3) | const VIRTUAL_SLIDE_PREFIX = '/@slidev/slides/'
method getContent (line 7) | async getContent({ data, utils }) {
FILE: packages/slidev/node/virtual/styles.ts
method getContent (line 8) | async getContent({ data, clientRoot, userRoot, roots }) {
FILE: packages/slidev/node/virtual/titles.ts
method getContent (line 5) | getContent({ data }) {
method getContent (line 25) | async getContent() {
FILE: packages/slidev/node/virtual/types.ts
type VirtualModuleTemplate (line 5) | interface VirtualModuleTemplate {
FILE: packages/slidev/node/vite/compilerFlagsVue.ts
function createVueCompilerFlagsPlugin (line 8) | function createVueCompilerFlagsPlugin(
FILE: packages/slidev/node/vite/components.ts
function createComponentsPlugin (line 6) | function createComponentsPlugin(
FILE: packages/slidev/node/vite/contextInjection.ts
function createContextInjectionPlugin (line 7) | function createContextInjectionPlugin(): Plugin {
FILE: packages/slidev/node/vite/extendConfig.ts
constant INCLUDE_GLOBAL (line 10) | const INCLUDE_GLOBAL = [
constant INCLUDE_LOCAL (line 21) | const INCLUDE_LOCAL = INCLUDE_GLOBAL.map(i => `@slidev/cli > @slidev/cli...
constant EXCLUDE_GLOBAL (line 24) | const EXCLUDE_GLOBAL = [
constant EXCLUDE_LOCAL (line 55) | const EXCLUDE_LOCAL = EXCLUDE_GLOBAL
constant ASYNC_MODULES (line 57) | const ASYNC_MODULES = [
function createConfigPlugin (line 63) | function createConfigPlugin(options: ResolvedSlidevOptions): Plugin {
FILE: packages/slidev/node/vite/hmrPatch.ts
function createHmrPatchPlugin (line 7) | function createHmrPatchPlugin(): Plugin {
FILE: packages/slidev/node/vite/icons.ts
function createIconsPlugin (line 4) | function createIconsPlugin(
FILE: packages/slidev/node/vite/index.ts
function ViteSlidevPlugin (line 23) | function ViteSlidevPlugin(
FILE: packages/slidev/node/vite/inspect.ts
function createInspectPlugin (line 3) | async function createInspectPlugin(
FILE: packages/slidev/node/vite/layoutWrapper.ts
function createLayoutWrapperPlugin (line 7) | function createLayoutWrapperPlugin(
FILE: packages/slidev/node/vite/loaders.ts
function createSlidesLoader (line 21) | function createSlidesLoader(
FILE: packages/slidev/node/vite/markdown.ts
function createMarkdownPlugin (line 9) | async function createMarkdownPlugin(
FILE: packages/slidev/node/vite/monacoTypes.ts
function createMonacoTypesLoader (line 10) | function createMonacoTypesLoader({ userRoot, utils }: ResolvedSlidevOpti...
FILE: packages/slidev/node/vite/monacoWrite.ts
function createMonacoWriterPlugin (line 8) | function createMonacoWriterPlugin({ userRoot }: ResolvedSlidevOptions): ...
FILE: packages/slidev/node/vite/patchMonacoSourceMap.ts
function cleanUrl (line 5) | function cleanUrl(url: string) {
function createPatchMonacoSourceMapPlugin (line 12) | function createPatchMonacoSourceMapPlugin(
FILE: packages/slidev/node/vite/remoteAssets.ts
function createRemoteAssetsPlugin (line 3) | async function createRemoteAssetsPlugin(
FILE: packages/slidev/node/vite/serverRef.ts
function createServerRefPlugin (line 6) | async function createServerRefPlugin(
FILE: packages/slidev/node/vite/staticCopy.ts
function createStaticCopyPlugin (line 5) | async function createStaticCopyPlugin(
FILE: packages/slidev/node/vite/unocss.ts
function createUnocssPlugin (line 5) | async function createUnocssPlugin(
FILE: packages/slidev/node/vite/vue.ts
function createVuePlugin (line 37) | async function createVuePlugin(
FILE: packages/types/src/builtin-layouts.ts
type BuiltinLayouts (line 1) | type BuiltinLayouts
FILE: packages/types/src/cli.ts
type CommonArgs (line 1) | interface CommonArgs {
type ExportArgs (line 6) | interface ExportArgs extends CommonArgs {
type BuildArgs (line 22) | interface BuildArgs extends ExportArgs {
FILE: packages/types/src/clicks.ts
type RawSingleAtValue (line 3) | type RawSingleAtValue = null | undefined | boolean | string | number
type RawRangeAtValue (line 4) | type RawRangeAtValue = null | undefined | false | [string | number, stri...
type RawAtValue (line 5) | type RawAtValue = RawSingleAtValue | RawRangeAtValue
type NormalizedSingleClickValue (line 7) | type NormalizedSingleClickValue
type NormalizedRangeClickValue (line 11) | type NormalizedRangeClickValue
type NormalizedAtValue (line 18) | type NormalizedAtValue
type ClicksElement (line 22) | type ClicksElement = Element | string
type ClicksInfo (line 24) | interface ClicksInfo {
type ClicksContext (line 55) | interface ClicksContext {
FILE: packages/types/src/code-runner.ts
type CodeRunnerContext (line 5) | interface CodeRunnerContext {
type CodeRunnerOutputHtml (line 20) | interface CodeRunnerOutputHtml {
type CodeRunnerOutputDom (line 29) | interface CodeRunnerOutputDom {
type CodeRunnerOutputError (line 36) | interface CodeRunnerOutputError {
type CodeRunnerOutputText (line 43) | interface CodeRunnerOutputText {
type CodeRunnerOutputTextArray (line 58) | type CodeRunnerOutputTextArray = CodeRunnerOutputText[]
type CodeRunnerOutput (line 60) | type CodeRunnerOutput = CodeRunnerOutputHtml | CodeRunnerOutputError | C...
type CodeRunnerOutputs (line 62) | type CodeRunnerOutputs = MaybeRefOrGetter<Arrayable<CodeRunnerOutput>>
type CodeRunner (line 64) | type CodeRunner = (code: string, ctx: CodeRunnerContext) => Awaitable<Co...
type CodeRunnerProviders (line 66) | type CodeRunnerProviders = Record<string, CodeRunner>
FILE: packages/types/src/config.ts
type ResolvedSlidevConfigSub (line 4) | interface ResolvedSlidevConfigSub {
type SlidevConfig (line 11) | interface SlidevConfig extends
type ResolvedFontOptions (line 16) | interface ResolvedFontOptions {
type ResolvedDrawingsOptions (line 27) | interface ResolvedDrawingsOptions {
type ResolvedExportOptions (line 34) | interface ResolvedExportOptions extends Omit<ExportArgs, 'entry' | 'them...
FILE: packages/types/src/context-menu.ts
type ContextMenuOption (line 3) | type ContextMenuOption = {
type ContextMenuItem (line 20) | type ContextMenuItem = ContextMenuOption | 'separator'
FILE: packages/types/src/frontmatter.ts
type Headmatter (line 4) | interface Headmatter extends HeadmatterConfig, Omit<Frontmatter, 'title'> {
type HeadmatterConfig (line 11) | interface HeadmatterConfig extends TransitionOptions {
type Frontmatter (line 329) | interface Frontmatter extends TransitionOptions {
type DrawingsOptions (line 422) | interface DrawingsOptions {
type FontOptions (line 451) | interface FontOptions {
type BuiltinSlideTransition (line 500) | type BuiltinSlideTransition = 'fade' | 'fade-out' | 'slide-up' | 'slide-...
type TransitionOptions (line 502) | interface TransitionOptions {
type TransitionGroupProps (line 522) | interface TransitionGroupProps {
type SeoMeta (line 546) | interface SeoMeta {
FILE: packages/types/src/hmr.ts
type CustomEventMap (line 4) | interface CustomEventMap {
FILE: packages/types/src/options.ts
type RootsInfo (line 6) | interface RootsInfo {
type SlidevEntryOptions (line 14) | interface SlidevEntryOptions {
type ResolvedSlidevOptions (line 51) | interface ResolvedSlidevOptions extends RootsInfo, SlidevEntryOptions {
type ResolvedSlidevUtils (line 64) | interface ResolvedSlidevUtils {
type SlidevServerOptions (line 75) | interface SlidevServerOptions {
FILE: packages/types/src/setups.ts
type AppContext (line 16) | interface AppContext {
type MonacoSetupReturn (line 21) | interface MonacoSetupReturn {
type NavOperations (line 25) | interface NavOperations {
type ShortcutOptions (line 41) | interface ShortcutOptions {
type ShikiContext (line 48) | interface ShikiContext {
type ShikiSetupReturn (line 56) | type ShikiSetupReturn
type TransformersSetupReturn (line 66) | interface TransformersSetupReturn {
type ShikiSetup (line 74) | type ShikiSetup = (shiki: ShikiContext) => Awaitable<ShikiSetupReturn | ...
type KatexSetup (line 75) | type KatexSetup = () => Awaitable<Partial<KatexOptions> | void>
type UnoSetup (line 76) | type UnoSetup = () => Awaitable<Partial<UnoCssConfig> | void>
type TransformersSetup (line 77) | type TransformersSetup = () => Awaitable<Partial<TransformersSetupReturn>>
type PreparserSetup (line 78) | type PreparserSetup = (context: {
type VitePluginsSetup (line 83) | type VitePluginsSetup = (options: ResolvedSlidevOptions) => VitePluginOp...
type MonacoSetup (line 86) | type MonacoSetup = (m: typeof monaco) => Awaitable<MonacoSetupReturn | v...
type AppSetup (line 87) | type AppSetup = (context: AppContext) => Awaitable<void>
type RootSetup (line 88) | type RootSetup = () => Awaitable<void>
type RoutesSetup (line 89) | type RoutesSetup = (routes: RouteRecordRaw[]) => RouteRecordRaw[]
type MermaidSetup (line 90) | type MermaidSetup = () => Awaitable<Partial<MermaidConfig> | void>
type MermaidRenderFn (line 91) | type MermaidRenderFn = (code: string, options: Record<string, any>) => A...
type MermaidRendererSetup (line 92) | type MermaidRendererSetup = () => Awaitable<MermaidRenderFn | void>
type ShortcutsSetup (line 93) | type ShortcutsSetup = (nav: NavOperations, defaultShortcuts: ShortcutOpt...
type CodeRunnersSetup (line 94) | type CodeRunnersSetup = (runners: CodeRunnerProviders) => Awaitable<Code...
type ContextMenuSetup (line 95) | type ContextMenuSetup = (items: ComputedRef<ContextMenuItem[]>) => Compu...
function defineSetup (line 97) | function defineSetup<Fn>(fn: Fn) {
FILE: packages/types/src/toc.ts
type TocItem (line 1) | interface TocItem {
FILE: packages/types/src/transform.ts
type MarkdownTransformContext (line 6) | interface MarkdownTransformContext {
type MarkdownTransformer (line 23) | type MarkdownTransformer = (ctx: MarkdownTransformContext) => Awaitable<...
FILE: packages/types/src/types.ts
type FrontmatterStyle (line 6) | type FrontmatterStyle = 'frontmatter' | 'yaml'
type SlideInfoBase (line 8) | interface SlideInfoBase {
type SourceSlideInfo (line 23) | interface SourceSlideInfo extends SlideInfoBase {
type SlideInfo (line 51) | interface SlideInfo extends SlideInfoBase {
type SlidePatch (line 70) | type SlidePatch = Partial<Pick<SlideInfoBase, 'content' | 'note' | 'fron...
type SlidevThemeMeta (line 82) | interface SlidevThemeMeta {
type SlidevThemeConfig (line 88) | type SlidevThemeConfig = Record<string, string | number>
type SlidevDetectedFeatures (line 90) | interface SlidevDetectedFeatures {
type SlidevMarkdown (line 103) | interface SlidevMarkdown {
type SlidevData (line 113) | interface SlidevData {
type SlidevPreparserExtension (line 130) | interface SlidevPreparserExtension {
type PreparserExtensionLoader (line 137) | type PreparserExtensionLoader = (headmatter: Record<string, unknown>, fi...
type RenderContext (line 139) | type RenderContext = 'none' | 'slide' | 'overview' | 'presenter' | 'prev...
type SlideRoute (line 141) | interface SlideRoute {
FILE: packages/types/src/vite.ts
type SlidevPluginOptions (line 13) | interface SlidevPluginOptions {
type UserConfig (line 28) | interface UserConfig {
FILE: packages/vscode/language-server/index.ts
method getLanguageSettings (line 15) | getLanguageSettings() {
FILE: packages/vscode/language-server/languagePlugin.ts
method getLanguageId (line 7) | getLanguageId() {
method createVirtualCode (line 10) | createVirtualCode(uri, languageId, snapshot) {
function lineToPos (line 31) | function lineToPos(line: number) {
FILE: packages/vscode/language-server/prettierService.ts
function create (line 4) | function create() {
FILE: packages/vscode/language-server/volar-service-yaml.ts
type Provide (line 9) | interface Provide {
function noop (line 13) | function noop(): undefined { }
function create (line 18) | function create({
function matchDocument (line 194) | function matchDocument(selector: DocumentSelector, document: TextDocumen...
FILE: packages/vscode/scripts/publish.ts
function publish (line 6) | async function publish() {
FILE: packages/vscode/src/commands.ts
function useCommands (line 13) | function useCommands() {
FILE: packages/vscode/src/composables/useDebouncedComputed.ts
function useDebouncedComputed (line 3) | function useDebouncedComputed<T>(source: () => T, delay: (newVal: T, old...
FILE: packages/vscode/src/composables/useDevServer.ts
type SlidevServer (line 11) | interface SlidevServer {
function useDevServer (line 17) | function useDevServer(project: SlidevProject) {
FILE: packages/vscode/src/composables/useFocusedSlide.ts
function gotoSlide (line 48) | async function gotoSlide(filepath: string, index: number) {
function focusSlide (line 61) | async function focusSlide(project: SlidevProject, no: number) {
FILE: packages/vscode/src/composables/useProjectFromDoc.ts
function getProjectFromDoc (line 7) | function getProjectFromDoc(doc: TextDocument | undefined) {
function useProjectFromDoc (line 22) | function useProjectFromDoc(doc: MaybeRefOrGetter<TextDocument | undefine...
FILE: packages/vscode/src/composables/useServerDetector.ts
type DetectedServerState (line 12) | interface DetectedServerState {
function redetect (line 24) | async function redetect(port: number) {
function getDetected (line 94) | function getDetected(project: SlidevProject) {
function allocPort (line 106) | async function allocPort() {
FILE: packages/vscode/src/html/error.ts
function generateErrorHtml (line 4) | function generateErrorHtml(message: string) {
FILE: packages/vscode/src/html/ready.ts
function generateReadyHtml (line 1) | function generateReadyHtml(port: number) {
FILE: packages/vscode/src/languageClient.ts
function useLanguageClient (line 10) | function useLanguageClient() {
FILE: packages/vscode/src/lmTools.ts
function registerSimpleTool (line 94) | function registerSimpleTool<T>(name: string, invoke: (input: T) => strin...
function resolveProjectFromEntry (line 112) | function resolveProjectFromEntry(entry: string) {
function formatList (line 138) | function formatList(items: Iterable<string>): string {
function formatObject (line 146) | function formatObject(obj: Record<string, string | number>): string {
FILE: packages/vscode/src/projects.ts
type SlidevProject (line 17) | interface SlidevProject {
function useProjects (line 33) | function useProjects() {
function rescanProjects (line 102) | async function rescanProjects() {
function addProject (line 127) | async function addProject(entry: string) {
function removeProject (line 186) | function removeProject(entry: string) {
function loadProject (line 196) | async function loadProject(entry: string) {
function askAddProject (line 209) | async function askAddProject(entry: string, message: string) {
FILE: packages/vscode/src/utils/findPossibleEntries.ts
function findPossibleEntries (line 6) | async function findPossibleEntries() {
FILE: packages/vscode/src/utils/findShallowestPath.ts
function findShallowestPath (line 1) | function findShallowestPath(paths: Iterable<string>) {
FILE: packages/vscode/src/utils/getFirstDisplayedChild.ts
function getFirstDisplayedChild (line 8) | function getFirstDisplayedChild(source: SourceSlideInfo) {
FILE: packages/vscode/src/utils/getSlidesTitle.ts
function getSlidesTitle (line 4) | function getSlidesTitle(data: LoadedSlidevData): string {
FILE: packages/vscode/src/utils/toRelativePath.ts
function toRelativePath (line 5) | function toRelativePath(path: string) {
FILE: packages/vscode/src/views/annotations.ts
function mergeSlideNumbers (line 37) | function mergeSlideNumbers(slides: { index: number }[]): string {
type CodeBlockInfo (line 49) | interface CodeBlockInfo {
function findCodeBlocks (line 55) | function findCodeBlocks(docText: string): CodeBlockInfo[] {
function updateCodeBlockLineNumbers (line 96) | function updateCodeBlockLineNumbers(editor: ReturnType<typeof useActiveT...
function getTextContent (line 164) | function getTextContent(source: SourceSlideInfo) {
FILE: packages/vscode/src/views/foldings.ts
method provideFoldingRanges (line 18) | async provideFoldingRanges(document: TextDocument) {
FILE: packages/vscode/src/views/previewWebview.ts
method onDidReceiveMessage (line 50) | async onDidReceiveMessage(data) {
function refresh (line 69) | async function refresh() {
function postSlidevMessage (line 80) | function postSlidevMessage(type: string, data: Record<string, unknown>) {
function useNavOperation (line 100) | function useNavOperation(operation: string, ...args: unknown[]) {
FILE: packages/vscode/src/views/projectsTree.ts
function getProjectTreeItem (line 17) | function getProjectTreeItem(project: SlidevProject): TreeItem {
function getFileTreeItem (line 34) | function getFileTreeItem(file: string): TreeItem {
FILE: packages/vscode/src/views/slidesTree.ts
type SlidesTreeNode (line 37) | interface SlidesTreeNode extends TreeViewNode {
method handleDrag (line 104) | handleDrag(source, dataTransfer) {
method handleDrop (line 113) | async handleDrop(target, dataTransfer) {
FILE: packages/vscode/syntaxes/codeblock-patch.ts
function generateCodeblockPatch (line 33) | function generateCodeblockPatch() {
FILE: packages/vscode/tsdown.config.ts
method handler (line 36) | async handler(source, importer) {
method onSuccess (line 45) | async onSuccess() {
FILE: scripts/pack.mjs
constant WORKSPACE_ROOT (line 5) | const WORKSPACE_ROOT = process.cwd()
constant PKG_ROOT (line 6) | const PKG_ROOT = resolve(WORKSPACE_ROOT, argv._[0])
function replaceDeps (line 16) | async function replaceDeps() {
function pack (line 32) | async function pack() {
FILE: scripts/remove-overridden-deps.mjs
constant OVERRIDDEN_PKGS (line 6) | const OVERRIDDEN_PKGS = [
function removeDeps (line 13) | async function removeDeps() {
FILE: shim.d.ts
type ComponentCustomProperties (line 18) | interface ComponentCustomProperties {
FILE: test/_tutils.ts
function createTransformContext (line 6) | function createTransformContext(code: string): MarkdownTransformContext {
FILE: test/fixtures/snippets/snippet.ts
function _foo (line 4) | function _foo() {
FILE: test/parser.test.ts
function configDiff (line 9) | function configDiff(v: SlidevConfig) {
function replaceCRLF (line 19) | function replaceCRLF(s: string) {
function parseWithExtension (line 116) | async function parseWithExtension(
function cov (line 172) | function cov(i: string, more = '.jpg') {
method transformRawLines (line 215) | transformRawLines(lines: string[]) {
method transformRawLines (line 221) | transformRawLines(lines: string[]) {
method transformSlide (line 286) | transformSlide(content: string, frontmatter: any) {
function project (line 311) | function project(s: string) {
FILE: test/utils.test.ts
function createFakeSource (line 96) | function createFakeSource(yaml: string) {
function expectFrontmatter (line 107) | function expectFrontmatter(slide: SlideInfo) {
Condensed preview — 649 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,519K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 37,
"preview": "github: antfu\nopencollective: slidev\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 781,
"preview": "---\nname: \"\\U0001F41E Bug report\"\nabout: Create a report to help us improve\ntitle: ''\ntype: Bug\nlabels: ['pending triage"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 190,
"preview": "contact_links:\n - name: Questions & Discussions\n url: https://github.com/slidevjs/slidev/discussions\n about: Use "
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 517,
"preview": "---\nname: \"\\U00002728 Feature request\"\nabout: Suggest an idea for this project\ntitle: ''\ntype: Feature\nassignees: ''\n---"
},
{
"path": ".github/stale.yml",
"chars": 364,
"preview": "daysUntilStale: 60\ndaysUntilClose: 7\nexemptLabels:\n - pinned\n - security\n - no-stale\n - no stale\n - pr welcome\n - "
},
{
"path": ".github/workflows/autofix.yml",
"chars": 621,
"preview": "name: autofix.ci\n\non:\n push:\n branches:\n - main\n pull_request:\n branches:\n - main\npermissions:\n conte"
},
{
"path": ".github/workflows/cr.yml",
"chars": 635,
"preview": "# Continuous Releases provided by https://pkg.pr.new\nname: CR (Continuous Releases)\non: [push, pull_request]\n\njobs:\n cr"
},
{
"path": ".github/workflows/release.yml",
"chars": 807,
"preview": "name: Release\n\non:\n push:\n tags:\n - 'v*'\n\njobs:\n release:\n permissions:\n id-token: write\n content"
},
{
"path": ".github/workflows/smoke.yml",
"chars": 4148,
"preview": "name: Production Smoke Test\n\non:\n push:\n branches:\n - main\n - master\n\n pull_request:\n branches:\n "
},
{
"path": ".github/workflows/test.yml",
"chars": 1516,
"preview": "name: Test\n\non:\n push:\n branches:\n - main\n - master\n\n pull_request:\n branches:\n - main\n - ma"
},
{
"path": ".gitignore",
"chars": 490,
"preview": ".DS_Store\n.eslintcache\n.idea\n.nuxt\n.output\n.slidev\n.vite-inspect\n*-export\n*.local\n*.pdf\n.env\nassets/demo\ncomponents.d.ts"
},
{
"path": ".vscode/extensions.json",
"chars": 191,
"preview": "{\n \"recommendations\": [\n \"antfu.vite\",\n \"Vue.volar\",\n \"antfu.iconify\",\n \"dbaeumer.vscode-eslint\",\n \"antf"
},
{
"path": ".vscode/launch.json",
"chars": 442,
"preview": "{\n \"version\": \"0.2.0\",\n \"configurations\": [\n {\n \"name\": \"Run Extension\",\n \"type\": \"extensionHost\",\n "
},
{
"path": ".vscode/settings.json",
"chars": 1413,
"preview": "{\n \"typescript.tsdk\": \"node_modules/typescript/lib\",\n \"files.associations\": {\n \"*.css\": \"postcss\"\n },\n \"unocss.ro"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 5216,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
},
{
"path": "CONTRIBUTING.md",
"chars": 2765,
"preview": "# Contributing\n\nExcited to hear that you are interested in contributing to this project! Thanks!\n\n## Documentation\n\nDocu"
},
{
"path": "LICENSE",
"chars": 1075,
"preview": "MIT License\n\nCopyright (c) 2020-PRESENT Anthony Fu\n\nPermission is hereby granted, free of charge, to any person obtainin"
},
{
"path": "README.md",
"chars": 5339,
"preview": "<br>\n<p align=\"center\">\n<a href=\"https://sli.dev\" target=\"_blank\">\n<img src=\"https://sli.dev/logo-title.png\" alt=\"Slidev"
},
{
"path": "cypress/e2e/examples/basic.spec.ts",
"chars": 6552,
"preview": "export {}\ndeclare global {\n // eslint-disable-next-line ts/no-namespace\n namespace Cypress {\n interface Chainable<S"
},
{
"path": "cypress/e2e/examples/presenter-resizer.spec.ts",
"chars": 1736,
"preview": "const LAYOUT_KEY = 'slidev-presenter-layout'\n\nfunction visitPresenter(layout: 1 | 2 | 3) {\n cy.visit('/presenter', {\n "
},
{
"path": "cypress/e2e/examples/smoke.spec.ts",
"chars": 789,
"preview": "context('Smoke test', () => {\n async function testAllSlides() {\n while (1) {\n let oldUrl, newUrl\n cy.get('"
},
{
"path": "cypress/fixtures/basic/components/DecorateWithLi.vue",
"chars": 70,
"preview": "<template>\n <li>Step b</li>\n <slot />\n <li>Step y</li>\n</template>\n"
},
{
"path": "cypress/fixtures/basic/components/WrapInClicks.vue",
"chars": 117,
"preview": "<template>\n <div style=\"border: 1px solid blue\">\n <v-clicks>\n <slot />\n </v-clicks>\n </div>\n</template>\n"
},
{
"path": "cypress/fixtures/basic/components/WrapInClicksDecorate.vue",
"chars": 148,
"preview": "<template>\n <v-clicks>\n <ul style=\"border: 1px solid red\">\n <li>A</li>\n <slot />\n <li>Z</li>\n </ul"
},
{
"path": "cypress/fixtures/basic/components/WrapInComponentInClicks.vue",
"chars": 215,
"preview": "<template>\n <v-clicks>\n <ol style=\"border: 1px solid green\">\n <li>Point a</li>\n <decorate-with-li>\n "
},
{
"path": "cypress/fixtures/basic/global.vue",
"chars": 194,
"preview": "<template>\n <div\n v-if=\"$slidev.nav.currentPage % 2 === 1\"\n class=\"absolute bottom-0 right-0 m-10 hover:text-red-"
},
{
"path": "cypress/fixtures/basic/package.json",
"chars": 422,
"preview": "{\n \"private\": true,\n \"scripts\": {\n \"dev\": \"nodemon -w '../../../packages/slidev/dist/*.js' --exec 'slidev --log=inf"
},
{
"path": "cypress/fixtures/basic/slides.md",
"chars": 2113,
"preview": "# Page 1\n\nHello World\n\n---\n\n# Page 2\n\n```html\n<style>\np {\n color: red;\n}\n</style>\n```\n\n`<p>` should have a green border"
},
{
"path": "cypress/fixtures/basic/sub/page1.md",
"chars": 20,
"preview": "# Sub page 1\n\n$x+2$\n"
},
{
"path": "cypress/fixtures/basic/sub/page2.md",
"chars": 55,
"preview": "---\nlayout: cover\n---\n\n# Sub page 2\n\n<Tweet :id=\"20\"/>\n"
},
{
"path": "cypress/fixtures/basic/vite.config.ts",
"chars": 125,
"preview": "import { defineConfig } from 'vite'\n\nexport default defineConfig({\n build: {\n manifest: true,\n minify: false,\n }"
},
{
"path": "cypress/tsconfig.json",
"chars": 176,
"preview": "{\n \"extends\": \"../tsconfig.json\",\n \"compilerOptions\": {\n \"types\": [\"cypress\"],\n \"noEmit\": true\n },\n \"include\":"
},
{
"path": "cypress.config.ts",
"chars": 223,
"preview": "import { defineConfig } from 'cypress'\n\nexport default defineConfig({\n e2e: {\n baseUrl: 'http://localhost:3041',\n "
},
{
"path": "demo/README.md",
"chars": 137,
"preview": "The best way of understanding Slidev is to try it, with the following command:\n\n```bash\nnpm init slidev\n```\n\nLearn more:"
},
{
"path": "demo/composable-vue/components/Connections.vue",
"chars": 2900,
"preview": "<script setup lang=\"ts\">\nimport { autoResetRef } from '@vueuse/core'\nimport { computed, ref, watch } from 'vue'\n\nconst a"
},
{
"path": "demo/composable-vue/components/DarkToggle.vue",
"chars": 528,
"preview": "<script setup lang=\"ts\">\nimport { useDarkMode } from '@slidev/client'\n\nconst { isDark, toggleDark } = useDarkMode()\n</sc"
},
{
"path": "demo/composable-vue/components/Marker.vue",
"chars": 202,
"preview": "<template>\n <span class=\"bg-$slidev-code-background px-2 py-1 rounded font-mono inline-block text-0.4em line-height-1em"
},
{
"path": "demo/composable-vue/components/MarkerCore.vue",
"chars": 78,
"preview": "<template>\n <Marker class=\"text-green-500\">\n Core\n </Marker>\n</template>\n"
},
{
"path": "demo/composable-vue/components/MarkerPattern.vue",
"chars": 80,
"preview": "<template>\n <Marker class=\"text-pink-500\">\n Pattern\n </Marker>\n</template>\n"
},
{
"path": "demo/composable-vue/components/MarkerTips.vue",
"chars": 79,
"preview": "<template>\n <Marker class=\"text-orange-400\">\n Tips\n </Marker>\n</template>\n"
},
{
"path": "demo/composable-vue/components/NumBox.vue",
"chars": 1508,
"preview": "<script setup lang=\"ts\">\nimport { useVModel } from '@vueuse/core'\n\nconst props = defineProps<{\n value: number\n label?:"
},
{
"path": "demo/composable-vue/components/VueUse.vue",
"chars": 407,
"preview": "<script setup lang=\"ts\">\ndefineProps<{\n name: string\n}>()\n</script>\n\n<template>\n <div class=\"px-2 -mx-2 mt-4 py-2\">\n "
},
{
"path": "demo/composable-vue/index.html",
"chars": 90,
"preview": "<head>\n <link rel=\"icon\" type=\"image/svg\" href=\"https://antfu.me/favicon.svg\" />\n</head>\n"
},
{
"path": "demo/composable-vue/package.json",
"chars": 543,
"preview": "{\n \"private\": true,\n \"scripts\": {\n \"dev\": \"nodemon -w '../../packages/slidev/dist/*.mjs' --exec 'slidev --log=info'"
},
{
"path": "demo/composable-vue/setup/monaco.ts",
"chars": 375,
"preview": "import { defineMonacoSetup } from '@slidev/types'\n\nexport default defineMonacoSetup((monaco) => {\n monaco.languages.typ"
},
{
"path": "demo/composable-vue/slides.md",
"chars": 20062,
"preview": "---\nlayout: cover\ndownload: 'https://antfu.me/talks/2021-04-29'\nhighlighter: shiki\nmonaco: true\ninfo: |\n ## Composable "
},
{
"path": "demo/starter/README.md",
"chars": 97,
"preview": "See [../../packages/create-app/template/README.md](../../packages/create-app/template/README.md)\n"
},
{
"path": "demo/starter/components/Counter.vue",
"chars": 648,
"preview": "<script setup lang=\"ts\">\nimport { ref } from 'vue'\n\nconst props = defineProps({\n count: {\n default: 0,\n },\n})\n\ncons"
},
{
"path": "demo/starter/package.json",
"chars": 508,
"preview": "{\n \"name\": \"slidev-demo\",\n \"private\": true,\n \"scripts\": {\n \"build\": \"slidev build\",\n \"dev\": \"nodemon -w '../../"
},
{
"path": "demo/starter/pages/imported-slides.md",
"chars": 365,
"preview": "# Imported Slides\n\nYou can split your slides.md into multiple files and organize them as you want using the `src` attrib"
},
{
"path": "demo/starter/slides.md",
"chars": 13566,
"preview": "---\n# try also 'default' to start simple\ntheme: seriph\n# random image from a curated Unsplash collection by Anthony\n# li"
},
{
"path": "demo/starter/snippets/external.ts",
"chars": 273,
"preview": "/* eslint-disable no-console */\n\n// #region snippet\n// Inside ./snippets/external.ts\nexport function emptyArray<T>(lengt"
},
{
"path": "demo/starter/style.css",
"chars": 32,
"preview": "/* add any global style here */\n"
},
{
"path": "demo/starter/vite.config.ts",
"chars": 85,
"preview": "import { defineConfig } from 'vite'\n\nexport default defineConfig({\n plugins: [],\n})\n"
},
{
"path": "demo/vue-runner/package.json",
"chars": 524,
"preview": "{\n \"private\": true,\n \"scripts\": {\n \"build\": \"slidev build\",\n \"dev\": \"nodemon -w '../../packages/slidev/dist/*.mj"
},
{
"path": "demo/vue-runner/setup/code-runners.ts",
"chars": 1289,
"preview": "/* eslint-disable no-new-func */\nimport { defineCodeRunnersSetup } from '@slidev/types'\n\nexport default defineCodeRunner"
},
{
"path": "demo/vue-runner/setup/shiki.ts",
"chars": 251,
"preview": "import type { ShikiSetupReturn } from '@slidev/types'\nimport { defineShikiSetup } from '@slidev/types'\n\nexport default d"
},
{
"path": "demo/vue-runner/slides.md",
"chars": 938,
"preview": "---\nlayout: default\n---\n\n# Simple Vue SFC Runner\n\n<!-- eslint-skip -->\n\n```vue {monaco-run}\n<script setup>\nimport { comp"
},
{
"path": "docs/.gitignore",
"chars": 54,
"preview": "node_modules\ndist\n.vitepress/@slidev\n.vitepress/cache\n"
},
{
"path": "docs/.vitepress/addons.ts",
"chars": 6887,
"preview": "import type { ThemeInfo } from './themes'\n\nexport type AddonInfo = Omit<ThemeInfo, 'previews'>\n\nexport const official: A"
},
{
"path": "docs/.vitepress/config.ts",
"chars": 4281,
"preview": "import type { DefaultTheme } from 'vitepress'\nimport { fileURLToPath } from 'node:url'\nimport { transformerTwoslash } fr"
},
{
"path": "docs/.vitepress/customizations.ts",
"chars": 1351,
"preview": "export default [\n {\n text: 'Configurations',\n link: '/custom/',\n },\n {\n text: 'Directory Structure',\n lin"
},
{
"path": "docs/.vitepress/pages.ts",
"chars": 1750,
"preview": "export const Guides = [\n {\n text: 'Why Slidev',\n link: '/guide/why',\n },\n {\n text: 'Getting Started',\n li"
},
{
"path": "docs/.vitepress/showcases.ts",
"chars": 12313,
"preview": "export interface ShowCaseInfo {\n title: string\n cover: string\n slidesLink?: string\n sourceLink?: string\n videoLink?"
},
{
"path": "docs/.vitepress/sidebar-gen.ts",
"chars": 3784,
"preview": "import type { DefaultTheme } from 'vitepress'\nimport { join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n"
},
{
"path": "docs/.vitepress/theme/components/AddonGallery.vue",
"chars": 457,
"preview": "<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { community, official } from '../../addons'\n\nconst props "
},
{
"path": "docs/.vitepress/theme/components/AddonInfo.vue",
"chars": 1714,
"preview": "<script setup lang=\"ts\">\nimport type { AddonInfo } from '../../addons'\n\ndefineProps<{\n addon: AddonInfo\n}>()\n</script>\n"
},
{
"path": "docs/.vitepress/theme/components/Demo.vue",
"chars": 5116,
"preview": "<script setup lang=\"ts\">\nimport type { SlidevMarkdown } from '@slidev/types'\nimport Center from '@slidev/client/layouts/"
},
{
"path": "docs/.vitepress/theme/components/DemoEditor.vue",
"chars": 741,
"preview": "<template>\n <div class=\"demo-editor\">\n <slot />\n </div>\n</template>\n\n<style lang=\"postcss\">\n.demo-editor {\n font-s"
},
{
"path": "docs/.vitepress/theme/components/DemoSlide.vue",
"chars": 577,
"preview": "<template>\n <div class=\"slide\">\n <div class=\"aspect-16/9\" />\n <div class=\"absolute top-0 left-0 right-0 bottom-0\""
},
{
"path": "docs/.vitepress/theme/components/Environment.vue",
"chars": 959,
"preview": "<script setup lang=\"ts\">\ndefineProps<{ type: 'node' | 'client' | 'both' }>()\n</script>\n\n<template>\n <details class=\"p4 "
},
{
"path": "docs/.vitepress/theme/components/FeatureTag.vue",
"chars": 2089,
"preview": "<script setup lang=\"ts\">\nimport { useData, withBase } from 'vitepress'\nimport { computed } from 'vue'\n\nconst props = def"
},
{
"path": "docs/.vitepress/theme/components/FeaturesAnimation.vue",
"chars": 193,
"preview": "<script setup lang=\"ts\">\nimport Inner from './FeaturesAnimationInner.vue'\n</script>\n\n<template>\n <div class=\"outer\">\n "
},
{
"path": "docs/.vitepress/theme/components/FeaturesAnimationInner.vue",
"chars": 1309,
"preview": "<script setup lang=\"ts\">\nimport type { Feature } from '../../../features/index.data'\nimport { withBase } from 'vitepress"
},
{
"path": "docs/.vitepress/theme/components/FeaturesOverview.vue",
"chars": 1149,
"preview": "<script setup lang=\"ts\">\nimport type { Feature } from '../../../features/index.data'\nimport { withBase } from 'vitepress"
},
{
"path": "docs/.vitepress/theme/components/LandingPage.vue",
"chars": 925,
"preview": "<template>\n <div class=\"xl:grid xl:grid-cols-[3fr_4fr] gap-4\" min-h-80vh px8 xl:px20 py5 max-w-100vw xl:max-w-450 mxa>\n"
},
{
"path": "docs/.vitepress/theme/components/Layout.vue",
"chars": 1986,
"preview": "<script setup lang=\"ts\">\nimport { Dropdown } from 'floating-vue'\nimport { useRoute } from 'vitepress'\nimport VPMenuLink "
},
{
"path": "docs/.vitepress/theme/components/LinkCard.vue",
"chars": 1320,
"preview": "<script setup lang=\"ts\">\nimport { withBase } from 'vitepress'\nimport { computed } from 'vue'\nimport { resolveLink } from"
},
{
"path": "docs/.vitepress/theme/components/LinkInline.vue",
"chars": 1220,
"preview": "<script setup lang=\"ts\">\nimport { Tooltip } from 'floating-vue'\nimport { withBase } from 'vitepress'\nimport { computed }"
},
{
"path": "docs/.vitepress/theme/components/SeeAlso.vue",
"chars": 209,
"preview": "<script setup lang=\"ts\">\ndefineProps<{\n links: string[]\n}>()\n</script>\n\n<template>\n <div class=\"op-90 mb--1\">\n See "
},
{
"path": "docs/.vitepress/theme/components/ShowCaseInfo.vue",
"chars": 1899,
"preview": "<script setup lang=\"ts\">\nimport type { ShowCaseInfo } from '../../showcases'\n\ndefineProps<{\n info: ShowCaseInfo\n}>()\n</"
},
{
"path": "docs/.vitepress/theme/components/ShowCases.vue",
"chars": 259,
"preview": "<script setup lang=\"ts\">\nimport { showcases } from '../../showcases'\n</script>\n\n<template>\n <div class=\"grid grid-cols-"
},
{
"path": "docs/.vitepress/theme/components/SlideContainer.vue",
"chars": 1113,
"preview": "<script setup lang=\"ts\">\nimport { useElementSize } from '@vueuse/core'\nimport { computed, ref } from 'vue'\n\nconst slideA"
},
{
"path": "docs/.vitepress/theme/components/TheTweet.vue",
"chars": 1552,
"preview": "<!--\nA simple wrapper for embedded Tweet\n\nUsage:\n\n<TheTweet id=\"20\" />\n-->\n\n<script setup lang=\"ts\">\nimport { isClient, "
},
{
"path": "docs/.vitepress/theme/components/ThemeGallery.vue",
"chars": 457,
"preview": "<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { community, official } from '../../themes'\n\nconst props "
},
{
"path": "docs/.vitepress/theme/components/ThemeInfo.vue",
"chars": 2276,
"preview": "<script setup lang=\"ts\">\nimport type { ThemeInfo } from '../../themes'\nimport { isClient, useIntervalFn } from '@vueuse/"
},
{
"path": "docs/.vitepress/theme/composables/dark.ts",
"chars": 72,
"preview": "import { useDark } from '@vueuse/core'\n\nexport const isDark = useDark()\n"
},
{
"path": "docs/.vitepress/theme/index.ts",
"chars": 496,
"preview": "import type { EnhanceAppContext } from 'vitepress'\nimport TwoSlash from '@shikijs/vitepress-twoslash/client'\nimport Them"
},
{
"path": "docs/.vitepress/theme/styles/custom.css",
"chars": 581,
"preview": ".icon-btn {\n --uno: inline-block cursor-pointer select-none important-outline-none;\n --uno: opacity-75 transition dura"
},
{
"path": "docs/.vitepress/theme/styles/demo.css",
"chars": 3119,
"preview": "html:not(.dark) {\n --prism-foreground: #393a34;\n --prism-background: #fafafa;\n --prism-inline-background: #f5f5f5;\n "
},
{
"path": "docs/.vitepress/theme/styles/vars.css",
"chars": 4525,
"preview": "/**\n * Customize default theme styling by overriding CSS variables:\n * https://github.com/vuejs/vitepress/blob/main/src/"
},
{
"path": "docs/.vitepress/themes.ts",
"chars": 27669,
"preview": "export interface ThemeInfo {\n id: string\n name: string\n description: string\n previews: string[]\n repo?: string\n au"
},
{
"path": "docs/.vitepress/utils.ts",
"chars": 1381,
"preview": "import { data as features } from '../features/index.data.js'\nimport { Advanced, Guides } from './pages'\n\nfunction remove"
},
{
"path": "docs/README.md",
"chars": 2815,
"preview": "# [sli.dev](https://sli.dev)\n\nDocumentation for [Slidev](https://github.com/slidevjs/slidev)\n\n## Translations\n\n> [!WARNI"
},
{
"path": "docs/builtin/cli.md",
"chars": 4213,
"preview": "# Slidev CLI\n\n`@slidev/cli` exposes a binary called `slidev` that you can use to develop, build, and export your slides."
},
{
"path": "docs/builtin/components.md",
"chars": 9306,
"preview": "# Components\n\nThis page lists all the built-in components provided by Slidev. These components can be **directly** used "
},
{
"path": "docs/builtin/layouts.md",
"chars": 3510,
"preview": "# Layouts\n\nThis page lists all the built-in layouts provided by Slidev. These layouts can be used via the `layout` optio"
},
{
"path": "docs/custom/config-code-runners.md",
"chars": 2526,
"preview": "# Configure Code Runners\n\n<Environment type=\"client\" />\n\nDefine code runners for custom languages in your Monaco Editor."
},
{
"path": "docs/custom/config-context-menu.md",
"chars": 1127,
"preview": "# Configure Context Menu\n\n<Environment type=\"client\" />\n\nCustomize the context menu items in Slidev.\n\nCreate `./setup/co"
},
{
"path": "docs/custom/config-fonts.md",
"chars": 2801,
"preview": "# Configure Fonts\n\nWhile you can use HTML and CSS to customize the fonts and style for your slides as you want, Slidev a"
},
{
"path": "docs/custom/config-highlighter.md",
"chars": 1844,
"preview": "# Configure Highlighter\n\nSlidev uses [Shiki](https://github.com/shikijs/shiki) as the code highlighter. It's a TextMate "
},
{
"path": "docs/custom/config-katex.md",
"chars": 460,
"preview": "# Configure KaTeX\n\n<Environment type=\"node\" />\n\nCreate `./setup/katex.ts` with the following content:\n\n```ts twoslash [s"
},
{
"path": "docs/custom/config-mermaid-renderer.md",
"chars": 701,
"preview": "# Configure Mermaid Renderer\n\n<Environment type=\"client\" />\n\n1. The user installs the Mermaid library they want to use. "
},
{
"path": "docs/custom/config-mermaid.md",
"chars": 1330,
"preview": "# Configure Mermaid\n\n<Environment type=\"client\" />\n\nCreate `./setup/mermaid.ts` with the following content:\n\n```ts twosl"
},
{
"path": "docs/custom/config-monaco.md",
"chars": 3299,
"preview": "# Configure Monaco\n\n<Environment type=\"client\" />\n\nCreate `./setup/monaco.ts` with the following content:\n\n```ts twoslas"
},
{
"path": "docs/custom/config-parser.md",
"chars": 7066,
"preview": "# Configure Pre-Parser\n\n::: info\nCustom pre-parsers are not supposed to be used too often. Usually you can use [Transfor"
},
{
"path": "docs/custom/config-routes.md",
"chars": 591,
"preview": "# Configure Routes\n\n<Environment type=\"client\" />\n\nAdd custom pages to the Slidev app.\n\n## Usage\n\nCreate `./setup/routes"
},
{
"path": "docs/custom/config-shortcuts.md",
"chars": 1179,
"preview": "# Configure Shortcuts\n\n<Environment type=\"client\" />\n\n## Getting started\n\nCreate `./setup/shortcuts.ts` with the followi"
},
{
"path": "docs/custom/config-transformers.md",
"chars": 1589,
"preview": "# Configure Transformers\n\n<Environment type=\"node\" />\n\nThis setup function allows you to define custom transformers for "
},
{
"path": "docs/custom/config-unocss.md",
"chars": 1628,
"preview": "# Configure UnoCSS\n\n<Environment type=\"node\" />\n\n[UnoCSS](https://unocss.dev) is now the default CSS framework for Slide"
},
{
"path": "docs/custom/config-vite.md",
"chars": 2863,
"preview": "# Configure Vite and Plugins\n\n<Environment type=\"node\" />\n\nSlidev is powered by [Vite](https://vitejs.dev/) under the ho"
},
{
"path": "docs/custom/config-vue.md",
"chars": 755,
"preview": "# Configure Vue App\n\n<Environment type=\"client\" />\n\nSlidev uses [Vue 3](https://v3.vuejs.org/) to render the application"
},
{
"path": "docs/custom/directory-structure.md",
"chars": 3769,
"preview": "# Directory Structure\n\nSlidev employs some directory structure conventions to minimize the configuration surface and to "
},
{
"path": "docs/custom/index.md",
"chars": 6481,
"preview": "# Customizations\n\nSlidev is fully customizable, from styling to tooling configurations. It allows you to configure the t"
},
{
"path": "docs/features/block-frontmatter.md",
"chars": 764,
"preview": "---\ndepends:\n - guide/syntax\nrelates:\n - features/prettier-plugin\ntags: [syntax]\ndescription: |\n Use a YAML code bloc"
},
{
"path": "docs/features/build-with-pdf.md",
"chars": 989,
"preview": "---\ndepends:\n - guide/exporting\n - guide/hosting\nrelates:\n - CLI export options: /builtin/cli#export\n - Headmatter e"
},
{
"path": "docs/features/bundle-remote-assets.md",
"chars": 986,
"preview": "---\nrelates:\n - vite-plugin-remote-assets: https://github.com/antfu/vite-plugin-remote-assets\ntags: [build]\ndescription"
},
{
"path": "docs/features/canvas-size.md",
"chars": 713,
"preview": "---\nrelates:\n - guide/faq#adjust-size\n - features/zoom-slide\n - features/transform-component\ntags: [layout]\ndescripti"
},
{
"path": "docs/features/click-marker.md",
"chars": 1166,
"preview": "---\ndepends:\n - guide/syntax#notes\n - guide/animations\nsince: v0.48.0\ntags: [presenter, animation]\ndescription: |\n Hi"
},
{
"path": "docs/features/code-block-line-numbers.md",
"chars": 825,
"preview": "---\ndepends:\n - guide/syntax#code-block\ntags: [codeblock]\ndescription: |\n Enable line numbering for all code blocks ac"
},
{
"path": "docs/features/code-block-max-height.md",
"chars": 647,
"preview": "---\ndepends:\n - guide/syntax#code-block\ntags: [codeblock, layout]\ndescription: |\n Set a maximum height for a code bloc"
},
{
"path": "docs/features/code-groups.md",
"chars": 4165,
"preview": "---\ndepends:\n - guide/syntax#code-block\ntags: [codeblock]\ndescription: |\n Group multiple code blocks and automatically"
},
{
"path": "docs/features/comark.md",
"chars": 839,
"preview": "---\nrelates:\n - Comark Syntax: https://comark.dev/syntax/markdown\n - '@comark/markdown-it': https://github.com/comarkd"
},
{
"path": "docs/features/direction-variant.md",
"chars": 1298,
"preview": "---\nrelates:\n - UnoCSS Variants: https://unocss.dev/config/variants#variants\nsince: v0.48.0\ntags: [navigation, styling]"
},
{
"path": "docs/features/draggable.md",
"chars": 2157,
"preview": "---\ntags: [layout]\ndescription: |\n Move, resize, and rotate elements by dragging them with the mouse.\n---\n\n# Draggable "
},
{
"path": "docs/features/drawing.md",
"chars": 1797,
"preview": "---\ndepends:\n - guide/ui#navigation-bar\nrelates:\n - drauu: https://github.com/antfu/drauu\ntags: [drawing]\ndescription:"
},
{
"path": "docs/features/eject-theme.md",
"chars": 738,
"preview": "---\ndepends:\n - guide/theme-addon\ntags: [theme, cli]\ndescription: |\n Eject the installed theme from your project to cu"
},
{
"path": "docs/features/frontmatter-merging.md",
"chars": 848,
"preview": "---\ndepends:\n - guide/syntax#importing-slides\n - features/importing-slides\ntags: [syntax]\ndescription: |\n Merge front"
},
{
"path": "docs/features/global-layers.md",
"chars": 2739,
"preview": "---\ntags: [navigation, layout]\ndescription: |\n Create custom components that persist across slides.\n---\n\n# Global Layer"
},
{
"path": "docs/features/icons.md",
"chars": 2798,
"preview": "---\nrelates:\n - Iconify: https://iconify.design/\n - Icones: https://icones.js.org/\n - unplugin-icons: https://github."
},
{
"path": "docs/features/import-snippet.md",
"chars": 1309,
"preview": "---\nrelates:\n - features/monaco-write\n - features/monaco-editor\nsince: v0.47.0\ntags: [codeblock, syntax]\ndescription: "
},
{
"path": "docs/features/importing-slides.md",
"chars": 1172,
"preview": "---\nrelates:\n - features/frontmatter-merging\ntags: [syntax]\ndescription: |\n Split your `slides.md` into multiple files"
},
{
"path": "docs/features/index.data.ts",
"chars": 1698,
"preview": "import { basename } from 'node:path'\nimport { createContentLoader } from 'vitepress'\n\nexport interface Feature {\n name:"
},
{
"path": "docs/features/index.md",
"chars": 2770,
"preview": "---\neditLink: false\nfooter: false\naside: false\noutline: false\nsidebar: false\npageClass: all-features-page\n---\n\n<script s"
},
{
"path": "docs/features/latex.md",
"chars": 1851,
"preview": "---\nrelates:\n - Demo: /demo/starter/11\n - KaTeX: https://katex.org/\ntags: [codeblock, syntax]\ndescription: |\n Slidev "
},
{
"path": "docs/features/line-highlighting.md",
"chars": 1259,
"preview": "---\ndepends:\n - guide/syntax#code-block\n - guide/animations\ntags: [codeblock, animation]\ndescription: |\n Highlight sp"
},
{
"path": "docs/features/mermaid.md",
"chars": 1062,
"preview": "---\nrelates:\n - Mermaid: https://mermaid.js.org/\n - Mermaid Live Editor: https://mermaid.live/\n - Demo Slide: https:/"
},
{
"path": "docs/features/monaco-editor.md",
"chars": 1644,
"preview": "---\ndepends:\n - guide/syntax#code-block\nrelates:\n - Monaco Editor: https://microsoft.github.io/monaco-editor/\n - Conf"
},
{
"path": "docs/features/monaco-run.md",
"chars": 1423,
"preview": "---\ndepends:\n - features/monaco-editor\n - guide/animations\nrelates:\n - Custom Code Runners: /custom/config-code-runne"
},
{
"path": "docs/features/monaco-write.md",
"chars": 789,
"preview": "---\ndepends:\n - features/monaco-editor\n - features/import-snippet\nrelates:\n - features/import-snippet\n - Custom Code"
},
{
"path": "docs/features/notes-auto-ruby.md",
"chars": 726,
"preview": "---\ntags: [notes, presenter]\ndescription: Automatically add `<ruby>` tags to your notes.\n---\n\n# Notes Auto Ruby\n\n> Avail"
},
{
"path": "docs/features/og-image.md",
"chars": 1056,
"preview": "---\nrelates:\n - features/seo-meta\ntags: ['SEO', head]\ndescription: |\n Set the Open Graph image for your slides.\n---\n\n#"
},
{
"path": "docs/features/plantuml.md",
"chars": 743,
"preview": "---\nrelates:\n - Plant UML: https://plantuml.com/\n - Plant UML Live Editor: https://plantuml.com/plantuml\n - Example S"
},
{
"path": "docs/features/prettier-plugin.md",
"chars": 1318,
"preview": "---\nrelates:\n - features/block-frontmatter\n - GitHub Repo: https://github.com/slidevjs/prettier-plugin\n - Prettier: h"
},
{
"path": "docs/features/recording.md",
"chars": 1324,
"preview": "---\ndepends:\n - guide/ui#navigation-bar\nrelates:\n - RecordRTC: https://github.com/muaz-khan/RecordRTC\n - WebRTC API: "
},
{
"path": "docs/features/remote-access.md",
"chars": 1706,
"preview": "---\nrelates:\n - guide/ui\n - CLI: builtin/cli\n - Cloudflare Quick Tunnels: https://developers.cloudflare.com/cloudflar"
},
{
"path": "docs/features/rough-marker.md",
"chars": 1311,
"preview": "---\ndepends:\n - guide/animations\nrelates:\n - Rough Notation: https://github.com/slidevjs/rough-notation\nsince: v0.48.0"
},
{
"path": "docs/features/seo-meta.md",
"chars": 1168,
"preview": "---\ndepends:\n - custom/index#headmatter\nrelates:\n - features/og-image\ntags: [SEO, head]\ndescription: |\n Configure SEO"
},
{
"path": "docs/features/shiki-magic-move.md",
"chars": 3083,
"preview": "---\ndepends:\n - guide/syntax#code-block\n - guide/animations\nrelates:\n - Shiki Magic Move: https://github.com/shikijs/"
},
{
"path": "docs/features/side-editor.md",
"chars": 434,
"preview": "---\ndepends:\n - guide/ui#navigation-actions\nrelates:\n - features/vscode-extension\ntags: [editor]\ndescription: |\n Edit"
},
{
"path": "docs/features/slide-hook.md",
"chars": 816,
"preview": "---\ndepends:\n - guide/global-context\ntags: [client-api]\ndescription: |\n Hooks to manage the slide lifecycle.\n---\n\n# Sl"
},
{
"path": "docs/features/slide-scope-style.md",
"chars": 1045,
"preview": "---\nrelates:\n - Vue's Scoped CSS: https://vuejs.org/api/sfc-css-features.html#scoped-css\n - UnoCSS directives: https:/"
},
{
"path": "docs/features/slot-sugar.md",
"chars": 1402,
"preview": "---\nrelates:\n - Vue's Named Slots: https://v3.vuejs.org/guide/component-slots.html\ntags: [layout, syntax]\ndescription: "
},
{
"path": "docs/features/timer.md",
"chars": 558,
"preview": "---\ntags: [presenter]\ndescription: Timer for the presenter mode.\n---\n\n# Presenter Timer\n\nSlidev provides a timer for the"
},
{
"path": "docs/features/transform-component.md",
"chars": 742,
"preview": "---\nrelates:\n - guide/faq#adjust-size\n - features/canvas-size\n - features/zoom-slide\ntags: [layout]\ndescription: |\n "
},
{
"path": "docs/features/twoslash.md",
"chars": 853,
"preview": "---\ndepends:\n - guide/syntax#code-block\nrelates:\n - TwoSlash: https://twoslash.netlify.app/\nsince: v0.46.0\ntags: [code"
},
{
"path": "docs/features/vscode-extension.md",
"chars": 4922,
"preview": "---\nrelates:\n - VS Code: https://code.visualstudio.com/\n - View in Marketplace: https://marketplace.visualstudio.com/i"
},
{
"path": "docs/features/zoom-slide.md",
"chars": 733,
"preview": "---\nrelates:\n - guide/faq#adjust-size\n - features/canvas-size\n - features/transform-component\ntags: [layout]\ndescript"
},
{
"path": "docs/guide/animations.md",
"chars": 12749,
"preview": "---\noutline: deep\n---\n\n# Animation\n\nAnimation is an essential part of slide presentations. Slidev provides a variety of "
},
{
"path": "docs/guide/component.md",
"chars": 1202,
"preview": "# Components in Slides\n\nOne of the most powerful features of Slidev is the ability to use Vue components directly in you"
},
{
"path": "docs/guide/exporting.md",
"chars": 7236,
"preview": "---\noutline: deep\n---\n\n# Exporting\n\nUsually the slides are displayed in a web browser, but you can also export them to P"
},
{
"path": "docs/guide/faq.md",
"chars": 2766,
"preview": "---\noutline: deep\n---\n\n# FAQ\n\n## Assets Handling {#assets-handling}\n\nYou may use static assets like images and videos in"
},
{
"path": "docs/guide/global-context.md",
"chars": 4083,
"preview": "# Global Context\n\nSlidev injects several global context values for advanced navigation controls.\n\n## Direct Usage {#dire"
},
{
"path": "docs/guide/hosting.md",
"chars": 6581,
"preview": "---\noutline: deep\n---\n\n# Building and Hosting\n\nSlidev is designed to run as a web server when you are editing or present"
},
{
"path": "docs/guide/index.md",
"chars": 6078,
"preview": "---\noutline: deep\n---\n\n# Getting Started\n\nSlidev <sup>(slide + dev, **/slaɪdɪv/**)</sup> is a web-based slides maker and"
},
{
"path": "docs/guide/layout.md",
"chars": 779,
"preview": "# Slide Layout\n\nLayouts in Slidev are used to define the structure for each slide. They are Vue components that wrap the"
},
{
"path": "docs/guide/syntax.md",
"chars": 4605,
"preview": "---\noutline: deep\n---\n\n# Syntax Guide\n\nSlidev's slides are written as Markdown files, which are called **Slidev Markdown"
},
{
"path": "docs/guide/theme-addon.md",
"chars": 2109,
"preview": "# Theme and Addons\n\nA slides project can have one theme and multiple addons. All of them can provide styles, components,"
},
{
"path": "docs/guide/ui.md",
"chars": 8860,
"preview": "---\noutline: deep\n---\n\n# User Interface\n\n## Navigation Bar {#navigation-bar}\n\nIn Play mode, move your mouse to the botto"
},
{
"path": "docs/guide/why.md",
"chars": 5955,
"preview": "---\noutline: deep\n---\n\n# Why Slidev\n\nThere have been lots of feature-rich WYSIWYG slides makers like [Microsoft PowerPoi"
},
{
"path": "docs/guide/work-with-ai.md",
"chars": 1710,
"preview": "# Work with AI\n\nThanks to Slidev being markdown-based, it works great with AI coding agents.\n\n## Skills\n\nSlidev provides"
},
{
"path": "docs/guide/write-addon.md",
"chars": 1830,
"preview": "# Writing Addons\n\n> Please read <LinkInline link=\"guide/theme-addon\" /> and <LinkInline link=\"guide/write-theme\" /> firs"
},
{
"path": "docs/guide/write-layout.md",
"chars": 919,
"preview": "# Writing Layouts\n\n> Please read <LinkInline link=\"guide/layout\" /> first.\n\nTo create a custom layout, simply create a n"
},
{
"path": "docs/guide/write-theme.md",
"chars": 3559,
"preview": "# Writing Themes\n\n> Please read <LinkInline link=\"guide/theme-addon\" /> first.\n\nEach slides project can only have one th"
},
{
"path": "docs/index.md",
"chars": 60,
"preview": "---\nlayout: home\nmarkdownStyles: false\n---\n\n<LandingPage />\n"
},
{
"path": "docs/netlify.toml",
"chars": 638,
"preview": "[build]\npublish = \".vitepress/dist\"\ncommand = \"pnpm run build\"\n\n[build.environment]\nNODE_VERSION = \"20\"\nPLAYWRIGHT_BROWS"
},
{
"path": "docs/package.json",
"chars": 1377,
"preview": "{\n \"name\": \"@slidev/docs\",\n \"type\": \"module\",\n \"version\": \"52.14.1\",\n \"license\": \"MIT\",\n \"funding\": \"https://github"
},
{
"path": "docs/resources/addon-gallery.md",
"chars": 727,
"preview": "---\naside: false\n---\n\n<script setup>\nimport AddonGallery from '../.vitepress/theme/components/AddonGallery.vue'\n</script"
},
{
"path": "docs/resources/covers.md",
"chars": 480,
"preview": "# Curated Covers\n\nWe curated a few cover images to demonstrate our starter template.\n\n\n\n```y"
},
{
"path": "docs/resources/learning.md",
"chars": 1192,
"preview": "# Learning Resources\n\n## English\n\n### Videos\n\n- [Slidev - one of the best presentation software and it is free!](https:/"
},
{
"path": "docs/resources/showcases.md",
"chars": 134,
"preview": "---\naside: false\n---\n\n# Showcases\n\nTalks / Presentations using Slidev.\n\n<!-- Edit in ./docs/.vitepress/showcases.ts -->\n"
},
{
"path": "docs/resources/theme-gallery.md",
"chars": 781,
"preview": "---\naside: false\n---\n\n<script setup>\nimport ThemeGallery from '../.vitepress/theme/components/ThemeGallery.vue'\n</script"
},
{
"path": "docs/tsconfig.json",
"chars": 587,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"ESNext\",\n \"jsx\": \"preserve\",\n \"lib\": [\"DOM\", \"ESNext\"],\n \"baseUrl\": \".\""
},
{
"path": "docs/uno.config.ts",
"chars": 566,
"preview": "import { defineConfig, presetAttributify, presetIcons, presetWebFonts, presetWind3, transformerDirectives } from 'unocss"
},
{
"path": "docs/vite.config.ts",
"chars": 1342,
"preview": "import { slidebars } from '.vitepress/config'\nimport UnoCSS from 'unocss/vite'\nimport IconsResolver from 'unplugin-icons"
},
{
"path": "eslint.config.js",
"chars": 909,
"preview": "import antfu from '@antfu/eslint-config'\n\nexport default antfu({\n pnpm: true,\n formatters: {\n markdown: true,\n c"
},
{
"path": "netlify.toml",
"chars": 766,
"preview": "[build]\npublish = \"docs/.vitepress/dist\"\ncommand = \"pnpm run build && pnpm run docs:build\"\n\n[build.environment]\nNODE_VER"
},
{
"path": "package.json",
"chars": 2975,
"preview": "{\n \"type\": \"module\",\n \"version\": \"52.14.1\",\n \"private\": true,\n \"packageManager\": \"pnpm@10.30.3\",\n \"engines\": {\n "
},
{
"path": "packages/client/.generated/unocss-tokens.ts",
"chars": 11047,
"preview": "/* eslint-disable eslint-comments/no-unlimited-disable */\n/* eslint-disable */\nexport default [\n \"!backdrop-blur-0px\",\n"
},
{
"path": "packages/client/App.vue",
"chars": 339,
"preview": "<script setup lang=\"ts\">\nimport { watchEffect } from 'vue'\nimport { themeVars } from './env'\nimport setupRoot from './se"
},
{
"path": "packages/client/README.md",
"chars": 334,
"preview": "# @slidev/client\n\n[](https://www.npmjs.co"
},
{
"path": "packages/client/builtin/Arrow.vue",
"chars": 1743,
"preview": "<!--\n\nSimple Arrow\n\n<arrow x1=\"10\" y1=\"20\" x2=\"100\" y2=\"200\" color=\"green\" width=\"3\" />\n\n<arrow v-bind=\"{ x1:10, y1:10, "
},
{
"path": "packages/client/builtin/AutoFitText.vue",
"chars": 1939,
"preview": "<!--\n[Experimental]\n\nThink this component as the TextBox you that will see\nin PowerPoint or Keynote. It will automatical"
},
{
"path": "packages/client/builtin/CodeBlockWrapper.vue",
"chars": 4395,
"preview": "<!--\nLine highlighting for code blocks.\n(auto transformed, you don't need to use this component directly)\n\nUsage:\n\n```ts"
},
{
"path": "packages/client/builtin/CodeGroup.vue",
"chars": 1480,
"preview": "<script setup lang=\"ts\">\nimport { onMounted, provide, ref, useTemplateRef } from 'vue'\nimport TitleIcon from '../interna"
},
{
"path": "packages/client/builtin/KaTexBlockWrapper.vue",
"chars": 3474,
"preview": "<!--\nLine highlighting for KaTex blocks/\n(auto transformed, you don't need to use this component directly)\n\nUsage:\n$$ {1"
},
{
"path": "packages/client/builtin/LightOrDark.vue",
"chars": 187,
"preview": "<script setup lang=\"ts\">\nimport { isDark } from '../logic/dark'\n</script>\n\n<template>\n <div>\n <slot v-if=\"isDark\" na"
},
{
"path": "packages/client/builtin/Link.vue",
"chars": 676,
"preview": "<!--\nCreate a link in the presentation\n\nUsage:\n\n<Link :to=\"5\" >Go to slide 5</Link>\n\n<Link :to=\"5\" title=\"Go to slide 5\""
},
{
"path": "packages/client/builtin/Mermaid.vue",
"chars": 1965,
"preview": "<!--\nMermaid\n(auto transformed, you don't need to use this component directly)\n\nUsage:\n\n```mermaid\npie\n\"Dogs\" : 386\n\"Cat"
},
{
"path": "packages/client/builtin/Monaco.vue",
"chars": 7939,
"preview": "<!--\nMonaco Editor\n(auto transformed, you don't need to use this component directly)\n\nUsage:\n\n```ts {monaco}\nconst your_"
}
]
// ... and 449 more files (download for full content)
About this extraction
This page contains the full source code of the slidevjs/slidev GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 649 files (1.3 MB), approximately 389.0k tokens, and a symbol index with 527 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.