├── .gitignore ├── .npmrc ├── LICENSE ├── README.md ├── components └── MarpSlide.vue ├── netlify.toml ├── package-lock.json ├── package.json ├── screencast.gif ├── setup └── preparser.ts └── slides.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | *.local 5 | index.html 6 | .remote-assets 7 | components.d.ts 8 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # for pnpm 2 | shamefully-hoist=true 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2021 Yuki Hattori 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # poc-marp-slidev 2 | 3 | Just a curious and crazy PoC to integrate [Marp](https://marp.app) with [Slidev](https://sli.dev). Render Marp slides on Slidev's presentation interface. 4 | 5 | **[https://poc-marp-slidev.netlify.app/](https://poc-marp-slidev.netlify.app/)** 6 | 7 |

8 | 9 |

10 | 11 | ## Try 12 | 13 | ``` 14 | npm i 15 | npm run dev 16 | ``` 17 | 18 | ## Is it useful? 19 | 20 | This PoC may be helpful if you like simple and minimum Markdown syntax provided by Marp, and want more enhanced presentation interface from Slidev: Overview, Recording, Drawing, Webcam PiP, etc... 21 | 22 | ## License 23 | 24 | [WTFPL](./LICENSE) 25 | -------------------------------------------------------------------------------- /components/MarpSlide.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 33 | 34 | 43 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build.environment] 2 | NODE_VERSION = "22" 3 | 4 | [build] 5 | publish = "dist" 6 | command = "npm run build" 7 | 8 | [[redirects]] 9 | from = "/*" 10 | to = "/index.html" 11 | status = 200 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "poc-marp-slidev", 4 | "scripts": { 5 | "dev": "slidev --open", 6 | "build": "slidev build" 7 | }, 8 | "dependencies": { 9 | "@marp-team/marp-core": "^4.0.1", 10 | "@slidev/cli": "^51.0.0", 11 | "@slidev/theme-default": "^0.25" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /screencast.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yhatt/poc-marp-slidev/c89362f9d5a9222bad78ec8612a6e11cf2f89911/screencast.gif -------------------------------------------------------------------------------- /setup/preparser.ts: -------------------------------------------------------------------------------- 1 | import { definePreparserSetup } from '@slidev/types' 2 | import { Marp } from '@marp-team/marp-core' 3 | import YAML from 'js-yaml' 4 | 5 | const slidevInheritedHeadmatters = [ 6 | 'author', 7 | 'browserExporter', 8 | 'colorSchema', 9 | 'contextMenu', 10 | 'download', 11 | 'drawings', 12 | 'editor', 13 | 'exportFilename', 14 | 'favicon', 15 | 'htmlAttrs', 16 | 'info', 17 | 'presenter', 18 | 'record', 19 | 'remote', 20 | 'remoteAssets', 21 | 'routerMode', 22 | 'selectable', 23 | 'title', 24 | 'titleTemplate', 25 | 'wakeLock', 26 | ] 27 | 28 | const marp = new Marp({ 29 | script: false, 30 | container: false, 31 | // html: true 32 | }) 33 | 34 | const escpaeChars = { 35 | '&': '&', 36 | '<': '<', 37 | '>': '>', 38 | '"': '"', 39 | "'": ''', 40 | '`': '`', 41 | '\n': ' ', 42 | '\r': ' ', 43 | } 44 | 45 | const escape = (str: string) => 46 | str.replace(/[&<>"'\n\r`]/g, (c) => escpaeChars[c]) 47 | 48 | export default definePreparserSetup(() => [ 49 | { 50 | name: 'Marp', 51 | async transformRawLines(lines: string[]) { 52 | const { html, css, comments } = marp.render(lines.join('\n'), { 53 | htmlAsArray: true, 54 | }) 55 | 56 | // Set up headmatters 57 | const slidevHeadmatters: Record = {} 58 | const matchedHeadmatter = lines 59 | .join('\n') 60 | .match(/^---.*\r?\n([\s\S]*?)---/) 61 | 62 | if (matchedHeadmatter) { 63 | const headmatter = YAML.load(matchedHeadmatter[1]) 64 | 65 | if (typeof headmatter === 'object') { 66 | for (const key of slidevInheritedHeadmatters) { 67 | if (key in headmatter) { 68 | slidevHeadmatters[key] = headmatter[key] 69 | } 70 | } 71 | } 72 | } 73 | 74 | // Apply aspect ratio 75 | const matchedViewBox = html[0].match(/viewBox="0\s+0\s+(\d+)\s+(\d+)"/) 76 | 77 | if (matchedViewBox) { 78 | slidevHeadmatters.aspectRatio = `${matchedViewBox[1]}/${matchedViewBox[2]}` 79 | 80 | const vw = Number.parseInt(matchedViewBox[1], 10) 81 | if (!Number.isNaN(vw)) slidevHeadmatters.canvasWidth = vw 82 | } 83 | 84 | // Replace Slidev Markdown to render Marp Markdown 85 | lines.splice( 86 | 0, 87 | lines.length, 88 | ...(slidevHeadmatters.length <= 0 89 | ? [] 90 | : ['---', JSON.stringify(slidevHeadmatters), '---']), 91 | ...html 92 | .map( 93 | (v, i) => 94 | '\n\n' + 95 | `\n\n` + 96 | `` 97 | ) 98 | .join('\n\n---\n\n') 99 | .split('\n') 100 | ) 101 | }, 102 | }, 103 | ]) 104 | -------------------------------------------------------------------------------- /slides.md: -------------------------------------------------------------------------------- 1 | --- 2 | marp: true 3 | title: Marp on Slidev PoC 4 | 5 | # Some Slidev headmatters are still supported 6 | favicon: https://marp.app/favicon.png 7 | info: "Just a curious and crazy PoC to integrate [Marp](https://marp.app) with [Slidev](https://sli.dev). Render Marp slides on Slidev's presentation interface." 8 | --- 9 | 10 | 13 | 14 | ![bg right 80% vertical](https://marp.app/assets/marp.svg) 15 | ![bg 60%](https://sli.dev/logo-title.png) 16 | 17 | # [Marp](https://marp.app) on [Slidev](https://sli.dev) 18 | 19 | An integration example of Marp for Slidev 20 | 21 | 22 | 23 | 24 | --- 25 | 26 | 29 | 30 | ## Yes, it's working! :rofl: 31 | 32 | This is Slidev but also Marp :smile: 33 | 34 | ![bg opacity](https://images.unsplash.com/photo-1627433488375-61f25ad84e29?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTYyODA1NDQzNw&ixlib=rb-1.2.1&q=80&w=1080) 35 | 36 | --- 37 | 38 | # How to write slides 39 | 40 | Split pages by horizontal ruler (`---`). It's very simple! :satisfied: 41 | 42 | ```markdown 43 | # Slide 1 44 | 45 | foobar 46 | 47 | --- 48 | 49 | # Slide 2 50 | 51 | foobar 52 | ``` 53 | 54 | --- 55 | 56 | ## Limitations 57 | 58 | - Cannot mix features for Markdown contents, both of Marp and Slidev. 59 | - An original Markdown will break if edited Markdown or notes through Slidev's web interface. 60 | --------------------------------------------------------------------------------