├── .browserslistrc ├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── README.md ├── babel.config.js ├── hello.mdx ├── jest.config.js ├── markdown-loader.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── favicon.ico ├── images │ └── think.gif └── index.html ├── src ├── App.vue ├── assets │ └── logo.png ├── cli.js ├── cli │ ├── colors.js │ ├── commands │ │ ├── build.js │ │ ├── dev.js │ │ ├── eject.js │ │ └── index.js │ ├── constants.js │ ├── emoji.js │ ├── index.js │ └── utils.js ├── components │ ├── Appear.vue │ ├── BGImage.js │ ├── CodeSample.js │ ├── CodeSurfer.vue │ └── Modes │ │ ├── GridMode.vue │ │ ├── OverviewMode.vue │ │ └── PresenterMode.vue ├── constants.js ├── index.js ├── layouts │ └── Split.js ├── main.js ├── mixins │ ├── InteractsWithSteps.js │ └── InteractsWithStorage.js ├── router.js ├── store.js ├── styles │ ├── components │ │ └── transitions.css │ ├── main.css │ ├── prism-atom-dark.css │ └── utilities │ │ └── transitions.css └── views │ └── Deck.vue ├── tailwind.config.js ├── tests └── unit │ ├── .eslintrc.js │ └── example.spec.js ├── theme.config.js ├── vue.config.js └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | end_of_line = lf 5 | trim_trailing_whitespace = true 6 | insert_final_newline = true 7 | max_line_length = 100 8 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | extends: [ 7 | 'plugin:vue/essential', 8 | 'eslint:recommended' 9 | ], 10 | rules: { 11 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 13 | 'vue/no-unused-components': 1 14 | }, 15 | parserOptions: { 16 | parser: 'babel-eslint' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | /lib 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MDX Deck 2 | 3 | ## Getting Started 4 | 5 | `npm i -D mdx-vue-deck` 6 | 7 | Create an [MDX][mdx] file and separate each slide with `---`. 8 | 9 | ````mdx 10 | # This is the title of my deck 11 | 12 | --- 13 | 14 | # About Me 15 | ``` 16 | 17 | --- 18 | 19 | import HelloWold from './components/HelloWold' 20 | 21 | ## 22 | 23 | # The end 24 | ```` 25 | 26 | Add a run script to your package.json with the MDX Deck CLI pointing to the .mdx file to start the dev server: 27 | 28 | ``` 29 | "scripts": { 30 | "eject": "mdx-vue-deck eject", 31 | "start": "mdx-vue-deck dev deck.mdx -c theme.config.js", 32 | "build": "mdx-vue-deck build deck.mdx -c theme.config.js" 33 | }, 34 | ``` 35 | 36 | ## Keyboard Shortcuts 37 | 38 | | Key | Description | 39 | | ----------- | -------------------------------------------- | 40 | | Left Arrow, Page Up, Shift + Space | Go to previous slide | 41 | | Right Arrow, Page Down, Space | Go to next slide | 42 | | Option + P | Toggle [Presenter Mode](#presenter-mode) | 43 | | Option + O | Toggle [Overview Mode](#overview-mode) | 44 | | Option + G | Toggle Grid Mode | 45 | 46 | 47 | ## References 48 | [mdx-deck](https://github.com/jxnblk/mdx-deck) 49 | 50 | ## License 51 | [MIT](https://choosealicense.com/licenses/mit/) 52 | 53 | [mdx]: https://mdxjs.com/ 54 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@babel/preset-env', 4 | '@vue/cli-plugin-babel/preset', 5 | ], 6 | plugins: [ 7 | ['prismjs', { 8 | languages: ['javascript', 'css', 'clike', 'bash', 'css-extras', 'php', 'jsx', 'diff', 'json'], 9 | // plugins: ['line-numbers'], 10 | // theme: 'okaidia', 11 | css: false 12 | }] 13 | ] 14 | }; 15 | -------------------------------------------------------------------------------- /hello.mdx: -------------------------------------------------------------------------------- 1 | import Appear from '@/components/Appear' 2 | import BGImage from '@/components/BGImage' 3 | import CodeSample from '@/components/CodeSample' 4 | import CodeSurfer from '@/components/CodeSurfer' 5 | import Split from '@/layouts/Split' 6 | 7 | # MDX VUE DECK 8 | 9 | ## Vue version MDX-based presentation decks 10 | 11 | --- 12 | 13 | # PRESENTATION DECKS 14 | 15 | --- 16 | 17 |

BUILT WITH MDX

18 | 19 | --- 20 | 21 | # List 22 | 23 | - Make bulleted lists 24 | 25 | - 1 26 | - 2 27 | - 3 28 | - 4 29 | 30 | - To help make your point 31 | 32 | --- 33 | 34 | ``` 35 | 36 | ``` 37 | 38 | --- 39 | 40 | ```jsx 41 | export default { 42 | data() { 43 | return { 44 | message: 'hello' 45 | } 46 | } 47 | } 48 | ``` 49 | 50 | --- 51 | 52 | ```diff 53 | +
z-40
54 | -
z-30
55 |
z-20
56 |
z-10
57 |
z-0
58 | ``` 59 | 60 | --- 61 | 62 | # Code Sample Component 63 | 64 | 65 |
66 |
z-40
67 |
z-30
68 |
z-20
69 |
z-10
70 |
z-0
71 |
72 |
73 | 74 | --- 75 | 76 | ![](https://images.unsplash.com/photo-1462331940025-496dfbfc7564?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1054&q=80) 77 | 78 | --- 79 | 80 | 81 | 82 | ![](https://images.unsplash.com/photo-1462331940025-496dfbfc7564?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1054&q=80) 83 | 84 | # SPLIT LAYOUT 85 | 86 | 87 | 88 | --- 89 | 90 | 91 | 92 | --- 93 | 94 | # Appear 95 | 96 | 97 |
  • One
  • 98 |
  • Two
  • 99 |
  • Three
  • 100 |
    101 | 102 | --- 103 | 104 | h(App) 133 | }) 134 | `} 135 | /> 136 | 137 | --- 138 | 139 | # Table 140 | 141 | |Month|Savings|Spending| 142 | |--- |--- |--- | 143 | |January|$100|$900| 144 | |July|$750|$1000| 145 | |December|$250|$300| 146 | |April|$400|$700| 147 | 148 | --- 149 | 150 |
    151 |

    152 | See the Pen 153 | Sprite Sheet by godkin.mo (@godkin-mo) 154 | on CodePen. 155 |

    156 | 157 |
    158 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: [ 3 | 'js', 4 | 'jsx', 5 | 'json', 6 | 'vue', 7 | ], 8 | transform: { 9 | '^.+\\.vue$': 'vue-jest', 10 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub', 11 | '^.+\\.jsx?$': 'babel-jest', 12 | }, 13 | transformIgnorePatterns: [ 14 | '/node_modules/', 15 | ], 16 | moduleNameMapper: { 17 | '^@/(.*)$': '/src/$1', 18 | }, 19 | snapshotSerializers: [ 20 | 'jest-serializer-vue', 21 | ], 22 | testMatch: [ 23 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)', 24 | ], 25 | testURL: 'http://localhost/', 26 | watchPlugins: [ 27 | 'jest-watch-typeahead/filename', 28 | 'jest-watch-typeahead/testname', 29 | ], 30 | }; 31 | -------------------------------------------------------------------------------- /markdown-loader.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | 3 | module.exports = function (src) { 4 | src = src.replace('wrapper', 'div') 5 | src = src.replace(/inlineCode/g, 'code') 6 | 7 | /* eslint-disable-next-line */ 8 | const imgRe = new RegExp(`()`, 'g') 9 | src = _.replace(src, imgRe, replacement => { 10 | const imgRe = /"src": "(.*)"/g 11 | const altRe = /"alt": "(.*)"/g 12 | 13 | const imgUrl = (imgRe.exec(replacement))[1] 14 | const imgAlt = altRe.exec(replacement) 15 | 16 | return `` 17 | }) 18 | 19 | /* eslint-disable-next-line */ 20 | const codeRe = new RegExp(`()`, 'g') 21 | src = _.replace(src, codeRe, replacement => { 22 | const classNameRe = /"className": "(.*)"/g 23 | const className = classNameRe.exec(replacement) 24 | 25 | const codeRe = /({`(.|\r|\n)*?`})<\/code>/g 26 | const code = (codeRe.exec(replacement))[2] 27 | 28 | return `${code}` 29 | }) 30 | 31 | /* eslint-disable-next-line */ 32 | const linkRe = new RegExp(`()`, 'g') 33 | src = _.replace(src, linkRe, replacement => { 34 | const urlRe = /"href": "(.*)"/g 35 | const url = urlRe.exec(replacement) 36 | 37 | return `${url[1]}` 38 | }) 39 | 40 | 41 | return src 42 | } 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mdx-vue-deck", 3 | "version": "0.1.12", 4 | "description": "MDX-based presentation decks", 5 | "author": "Jack Fong", 6 | "scripts": { 7 | "build": "babel-node src/cli.js build hello.mdx", 8 | "test:unit": "vue-cli-service test:unit", 9 | "babelify": "babel src --out-dir lib --copy-files", 10 | "help": "babel-node src/cli.js", 11 | "start": "babel-node src/cli.js dev hello.mdx" 12 | }, 13 | "main": "lib/index.js", 14 | "dependencies": { 15 | "@babel/cli": "^7.6.0", 16 | "@babel/node": "^7.6.1", 17 | "@babel/preset-env": "^7.6.0", 18 | "@mdx-js/vue-loader": "^1.4.0", 19 | "@vue/cli-plugin-babel": "^4.0.5", 20 | "@vue/cli-service": "^4.0.5", 21 | "@vue/test-utils": "1.0.0-beta.29", 22 | "babel-core": "7.0.0-bridge.0", 23 | "babel-eslint": "^10.0.3", 24 | "babel-jest": "^25.0.0", 25 | "babel-plugin-prismjs": "^1.1.1", 26 | "chalk": "^2.4.2", 27 | "core-js": "^3.3.2", 28 | "execa": "^2.0.4", 29 | "fs-extra": "^8.1.0", 30 | "markdown-it": "^9.1.0", 31 | "meow": "^5.0.0", 32 | "mousetrap": "^1.6.3", 33 | "node-emoji": "^1.10.0", 34 | "postcss-import": "^12.0.1", 35 | "postcss-nesting": "^7.0.1", 36 | "prettify-xml": "^1.2.0", 37 | "prismjs": "^1.17.1", 38 | "serialize-javascript": "^2.1.2", 39 | "tailwindcss": "^1.1.2", 40 | "tailwindcss-typography": "^2.2.0", 41 | "vue": "^2.6.10", 42 | "vue-router": "^3.0.3", 43 | "vue-template-compiler": "^2.6.10", 44 | "vuex": "^3.0.1" 45 | }, 46 | "bin": { 47 | "mdx-vue-deck": "lib/cli.js" 48 | }, 49 | "license": "MIT", 50 | "repository": "https://github.com/godkinmo/mdx-vue-deck.git", 51 | "devDependencies": { 52 | "@vue/cli-plugin-eslint": "^4.0.5", 53 | "eslint": "^6.6.0", 54 | "eslint-plugin-vue": "^6.0.1" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('postcss-import'), 4 | // Watching theme file for changes 5 | function (css, opts) { 6 | const resolvedPath = process.env.__TAILWIND_THEME_CONFIG_PATH__ 7 | 8 | delete require.cache[require.resolve(resolvedPath)] 9 | opts.messages.push({ 10 | type: 'dependency', 11 | file: resolvedPath, 12 | parent: css.source.input.file, 13 | }) 14 | }, 15 | require('tailwindcss')('./tailwind.config.js'), 16 | require('autoprefixer'), 17 | require('postcss-nesting')() 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godkinmo/mdx-vue-deck/8c72ee37e4e6919daa67891ff0b13f248a6b4a51/public/favicon.ico -------------------------------------------------------------------------------- /public/images/think.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godkinmo/mdx-vue-deck/8c72ee37e4e6919daa67891ff0b13f248a6b4a51/public/images/think.gif -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | mdx-vue-deck 9 | 10 | 11 | 14 |
    15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godkinmo/mdx-vue-deck/8c72ee37e4e6919daa67891ff0b13f248a6b4a51/src/assets/logo.png -------------------------------------------------------------------------------- /src/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import meow from 'meow' 4 | import pkg from '../package.json' 5 | import commands from './cli/commands' 6 | import * as colors from './cli/colors' 7 | import * as utils from './cli/utils' 8 | 9 | const cli = meow( 10 | ` 11 | mdx-vue-deck ${colors.info(pkg.version)} 12 | 13 | Usage: 14 | mdx-vue-deck deck.mdx 15 | mdx-vue-deck deck.mdx -c tailwind.config.js 16 | mdx-vue-deck build deck.mdx 17 | mdx-vue-deck eject 18 | 19 | Options: 20 | -h --host Dev server host 21 | -p --port Dev server port 22 | -c --config Use custom theme config file 23 | --no-open Prevent from opening in default browser 24 | `, 25 | { 26 | description: false, 27 | flags: { 28 | port: { 29 | type: 'string', 30 | alias: 'p', 31 | default: '8000', 32 | }, 33 | host: { 34 | type: 'string', 35 | alias: 'h', 36 | default: 'localhost', 37 | }, 38 | open: { 39 | type: 'boolean', 40 | alias: 'o', 41 | default: true, 42 | }, 43 | config: { 44 | type: 'string', 45 | alias: 'c', 46 | } 47 | }, 48 | } 49 | ) 50 | 51 | const cmd = cli.input[0] 52 | const filename = cli.input[1] 53 | 54 | if (cli.input.length === 0) { 55 | cli.showHelp(0) 56 | } 57 | 58 | if (['dev', 'build'].includes(cmd)) { 59 | if (typeof filename === 'undefined') { 60 | cli.showHelp(0) 61 | } 62 | 63 | utils.prepareVueCliService(cli, filename) 64 | } 65 | 66 | commands[cmd].run(cli) 67 | -------------------------------------------------------------------------------- /src/cli/colors.js: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk' 2 | 3 | /** 4 | * Applies colors to emphasize 5 | * 6 | * @param {...string} msgs 7 | */ 8 | export function bold(...msgs) { 9 | return chalk.bold(...msgs) 10 | } 11 | 12 | /** 13 | * Applies colors to inform 14 | * 15 | * @param {...string} msgs 16 | */ 17 | export function info(...msgs) { 18 | return chalk.bold.cyan(...msgs) 19 | } 20 | 21 | /** 22 | * Applies colors to signify error 23 | * 24 | * @param {...string} msgs 25 | */ 26 | export function error(...msgs) { 27 | return chalk.bold.red(...msgs) 28 | } 29 | 30 | /** 31 | * Applies colors to represent a file 32 | * 33 | * @param {...string} msgs 34 | */ 35 | export function file(...msgs) { 36 | return chalk.bold.magenta(...msgs) 37 | } 38 | -------------------------------------------------------------------------------- /src/cli/commands/build.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import * as utils from '../utils' 3 | 4 | export function run() { 5 | utils.vueCliService('build').then(() => { 6 | const source = path.resolve(__dirname, '../../../dist') 7 | const destination = path.join(process.cwd(), 'dist') 8 | if (source === destination) return 9 | utils.copyFile(source, destination) 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /src/cli/commands/dev.js: -------------------------------------------------------------------------------- 1 | import * as utils from '../utils' 2 | 3 | export function run({ flags }) { 4 | utils.vueCliService( 5 | 'serve', 6 | '--host', 7 | flags.host, 8 | '--port', 9 | flags.port, 10 | flags.open && '--open' 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /src/cli/commands/eject.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | 3 | import * as constants from '../../constants' 4 | import * as colors from '../colors' 5 | import * as emoji from '../emoji' 6 | import * as utils from '../utils' 7 | 8 | export function run() { 9 | utils.header() 10 | 11 | const file = path.join(process.cwd(), 'theme.config.js') 12 | const simplePath = 'theme.config.js' 13 | 14 | utils.exists(file) && utils.die(colors.file(simplePath), 'already exists.') 15 | 16 | utils.copyFile(constants.tailwindThemeConfigFile, file) 17 | 18 | utils.log() 19 | utils.log(emoji.yes, 'Created Tailwind theme config file:', colors.file(simplePath)) 20 | 21 | utils.footer() 22 | } 23 | -------------------------------------------------------------------------------- /src/cli/commands/index.js: -------------------------------------------------------------------------------- 1 | import * as dev from './dev' 2 | import * as build from './build' 3 | import * as eject from './eject' 4 | 5 | export default { dev, build, eject } 6 | -------------------------------------------------------------------------------- /src/cli/constants.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | 3 | export const tailwindThemeConfigFile = path.resolve(__dirname, './theme.config.js') 4 | -------------------------------------------------------------------------------- /src/cli/emoji.js: -------------------------------------------------------------------------------- 1 | import { get } from 'node-emoji' 2 | 3 | export const yes = get('white_check_mark') 4 | export const no = get('no_entry_sign') 5 | export const go = get('rocket') 6 | export const pack = get('package') 7 | export const disk = get('floppy_disk') 8 | -------------------------------------------------------------------------------- /src/cli/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import meow from 'meow' 4 | import pkg from '../../package.json' 5 | import commands from './commands' 6 | import * as colors from './colors' 7 | 8 | const cli = meow( 9 | ` 10 | mdx-vue-deck ${colors.info(pkg.version)} 11 | 12 | Usage: 13 | mdx-vue-deck deck.mdx 14 | mdx-vue-deck deck.mdx -c tailwind.config.js 15 | mdx-vue-deck build deck.mdx 16 | mdx-vue-deck eject 17 | 18 | Options: 19 | -h --host Dev server host 20 | -p --port Dev server port 21 | -c --config Use custom theme config file 22 | --no-open Prevent from opening in default browser 23 | `, 24 | { 25 | description: false, 26 | flags: { 27 | port: { 28 | type: 'string', 29 | alias: 'p', 30 | default: '8000', 31 | }, 32 | host: { 33 | type: 'string', 34 | alias: 'h', 35 | default: 'localhost', 36 | }, 37 | open: { 38 | type: 'boolean', 39 | alias: 'o', 40 | default: true, 41 | }, 42 | config: { 43 | type: 'string', 44 | alias: 'c', 45 | } 46 | }, 47 | } 48 | ) 49 | 50 | const cmd = cli.input[0] 51 | const filename = cli.input[1] 52 | 53 | if (cli.input.length === 0) { 54 | cli.showHelp(0) 55 | } 56 | 57 | process.env.__TAILWIND_THEME_CONFIG_PATH__ = './theme.config.js' 58 | 59 | commands[cmd].run(cli, filename) 60 | -------------------------------------------------------------------------------- /src/cli/utils.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import execa from 'execa' 3 | import { copySync, existsSync } from 'fs-extra' 4 | 5 | import * as colors from './colors' 6 | import * as emoji from './emoji' 7 | import packageJson from '../../package.json' 8 | 9 | /** 10 | * Prints messages to console. 11 | * 12 | * @param {...string} [msgs] 13 | */ 14 | export function log(...msgs) { 15 | console.log(' ', ...msgs) 16 | } 17 | 18 | /** 19 | * Prints application header to console. 20 | */ 21 | export function header() { 22 | log() 23 | log(colors.bold(packageJson.name), colors.info(packageJson.version)) 24 | } 25 | 26 | /** 27 | * Prints application footer to console. 28 | */ 29 | export function footer() { 30 | log() 31 | } 32 | 33 | /** 34 | * Prints error messages to console. 35 | * 36 | * @param {...string} [msgs] 37 | */ 38 | export function error(...msgs) { 39 | log() 40 | console.error(' ', emoji.no, colors.error(msgs.join(' '))) 41 | } 42 | 43 | /** 44 | * Kills the process. Optionally prints error messages to console. 45 | * 46 | * @param {...string} [msgs] 47 | */ 48 | export function die(...msgs) { 49 | msgs.length && error(...msgs) 50 | footer() 51 | process.exit(1) 52 | } 53 | 54 | /** 55 | * Checks if path exists. 56 | * 57 | * @param {string} path 58 | * @return {boolean} 59 | */ 60 | export function exists(path) { 61 | return existsSync(path) 62 | } 63 | 64 | /** 65 | * Copies file source to destination. 66 | * 67 | * @param {string} source 68 | * @param {string} destination 69 | */ 70 | export function copyFile(source, destination) { 71 | copySync(source, destination) 72 | } 73 | 74 | /** 75 | * Run vue-cli-service command 76 | */ 77 | export function vueCliService(...args) { 78 | return execa('vue-cli-service', args.filter(Boolean), { 79 | cwd: path.resolve(__dirname, '../../'), 80 | stdio: 'inherit', 81 | preferLocal: true, 82 | env: { 83 | SRC_DECK: process.env.__SRC__ 84 | } 85 | }) 86 | } 87 | 88 | export function prepareVueCliService({ flags }, filename) { 89 | if (flags.config) { 90 | const tailwindThemeConfig = path.resolve(flags.config) 91 | !exists(tailwindThemeConfig) && die(colors.file(flags.config), 'does not exists') 92 | process.env.__TAILWIND_THEME_CONFIG_PATH__ = tailwindThemeConfig 93 | } else { 94 | process.env.__TAILWIND_THEME_CONFIG_PATH__ = path.resolve('./theme.config.js') 95 | } 96 | 97 | const source = path.resolve(filename) 98 | 99 | !exists(source) && die(colors.file(filename), 'does not exists.') 100 | 101 | process.env.__SRC__ = source 102 | } 103 | -------------------------------------------------------------------------------- /src/components/Appear.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 42 | -------------------------------------------------------------------------------- /src/components/BGImage.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: ['src'], 3 | 4 | render(createElement) { 5 | return createElement('div', { 6 | attrs: { 7 | class: 'w-full', 8 | }, 9 | }, [ 10 | createElement('img', { 11 | attrs: { 12 | class: 'mx-auto h-full', 13 | src: this.src, 14 | } 15 | }) 16 | ]) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/components/CodeSample.js: -------------------------------------------------------------------------------- 1 | const md = require('markdown-it')() 2 | const prettifyXml = require('prettify-xml') 3 | 4 | const getChildrenTextContent = function (children, tag = 'div', classAttr) { 5 | return children.map(function (node) { 6 | return node.children 7 | ? getChildrenTextContent(node.children, node.tag, node.data ? node.data.class : '') 8 | : `<${tag} class="${classAttr}">${node.text}` 9 | }).join('') 10 | } 11 | 12 | const getCodeEl = (createElement, slots, { lineNumbers }) => { 13 | if (slots.default[1]) { 14 | const codeLanguageClass = slots.default[1].children[0].data.className 15 | 16 | return createElement('div', { 17 | attrs: { class: `${codeLanguageClass}` } 18 | }, [slots.default[1]]) 19 | } else { 20 | const prettyHTML = prettifyXml(getChildrenTextContent(slots.default), { 21 | indent: 4, newline: '\n ' 22 | }) 23 | const codeHTML = md.render(` 24 | ${prettyHTML} 25 | `) 26 | return createElement('div', { 27 | attrs: { class: `language-html ${lineNumbers ? 'line-numbers' : ''}` }, 28 | domProps: { 29 | innerHTML: codeHTML 30 | }, 31 | }) 32 | } 33 | } 34 | 35 | export default { 36 | render(createElement) { 37 | return createElement('div', { 38 | attrs: { 39 | class: 'relative w-full max-w-3xl overflow-hidden mb-8' 40 | } 41 | }, [ 42 | createElement('div', { 43 | attrs: { 44 | class: 'bg-white rounded-t-lg overflow-hidden border-t border-l border-r border-gray-400 p-4' 45 | } 46 | }, [this.$slots.default[0]]), 47 | createElement('div', { 48 | attrs: { 49 | class: 'rounded-b-lg bg-gray-800' 50 | } 51 | }, [ 52 | getCodeEl(createElement, this.$slots, { 53 | lineNumbers: this.$attrs.lineNumbers || false 54 | }) 55 | ]), 56 | ]) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/components/CodeSurfer.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 98 | -------------------------------------------------------------------------------- /src/components/Modes/GridMode.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 26 | -------------------------------------------------------------------------------- /src/components/Modes/OverviewMode.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 54 | -------------------------------------------------------------------------------- /src/components/Modes/PresenterMode.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 28 | -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | 3 | export const tailwindThemeConfigFile = path.resolve(__dirname, '../theme.config.js') 4 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export { 2 | } 3 | -------------------------------------------------------------------------------- /src/layouts/Split.js: -------------------------------------------------------------------------------- 1 | export default { 2 | render(createElement) { 3 | return createElement('div', { 4 | attrs: { 5 | class: 'w-full flex flex-row -mx-2' 6 | } 7 | }, [ 8 | createElement('div', { 9 | attrs: { 10 | class: 'w-1/2 mx-2 flex items-center justify-center' 11 | } 12 | }, [this.$slots.default[0]]), 13 | 14 | createElement('div', { 15 | attrs: { 16 | class: 'w-1/2 mx-2 flex flex-col items-center justify-center' 17 | } 18 | }, [ 19 | ...this.$slots.default.slice(1) 20 | ]) 21 | ]) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import store from './store' 5 | 6 | Vue.config.productionTip = false 7 | 8 | new Vue({ 9 | router, 10 | store, 11 | render: h => h(App), 12 | }).$mount('#app') 13 | -------------------------------------------------------------------------------- /src/mixins/InteractsWithSteps.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data: () => ({ 3 | componentPage: null, 4 | }), 5 | 6 | mounted() { 7 | this.$nextTick(() => { 8 | const deckEl = this.$el.closest('.markdown') 9 | if (deckEl) { 10 | this.componentPage = Array.from(deckEl.parentElement.children).indexOf(deckEl) + 1 11 | } 12 | }) 13 | }, 14 | 15 | beforeDestroy() { 16 | this.$store.commit('setSteps', 0) 17 | }, 18 | 19 | watch: { 20 | active(value) { 21 | if (value) { 22 | this.$store.commit('setSteps', this.steps) 23 | } 24 | }, 25 | step(step) { 26 | if (this.active) { 27 | history.pushState({}, '', `/#/${this.currentPage}${step ? `.${step}`: ''}`); 28 | } 29 | }, 30 | }, 31 | 32 | computed: { 33 | currentPage() { 34 | return parseInt(this.$route.params.page) 35 | }, 36 | active() { 37 | return this.currentPage === this.componentPage 38 | }, 39 | step() { 40 | return this.$store.state.step || 0 41 | } 42 | }, 43 | } 44 | -------------------------------------------------------------------------------- /src/mixins/InteractsWithStorage.js: -------------------------------------------------------------------------------- 1 | const keys = { 2 | page: 'mdx-deck-page', 3 | step: 'mdx-deck-step', 4 | } 5 | 6 | export default { 7 | data: () => ({ 8 | focused: true, 9 | }), 10 | 11 | mounted() { 12 | window.addEventListener('focus', this.handleFocus) 13 | window.addEventListener('blur', this.handleBlur) 14 | }, 15 | 16 | beforeDestroy() { 17 | window.removeEventListener('focus', this.handleFocus) 18 | window.removeEventListener('blur', this.handleBlur) 19 | }, 20 | 21 | watch: { 22 | '$store.state.currentPage'(page) { 23 | if (!this.focused) return 24 | localStorage.setItem(keys.page, page) 25 | }, 26 | '$store.state.step' (step) { 27 | localStorage.setItem(keys.step, step) 28 | }, 29 | focused: { 30 | immediate: true, 31 | handler(focused) { 32 | if (!focused){ 33 | window.addEventListener('storage', this.handleStorageChange) 34 | } else { 35 | window.removeEventListener('storage', this.handleStorageChange) 36 | } 37 | } 38 | }, 39 | }, 40 | 41 | methods: { 42 | handleFocus() { 43 | this.focused = true 44 | }, 45 | handleBlur() { 46 | this.focused = false 47 | }, 48 | handleStorageChange(e) { 49 | const n = parseInt(e.newValue, 10) 50 | 51 | if (isNaN(n)) return 52 | 53 | switch (e.key) { 54 | case keys.page: 55 | this.$router.push({ name: 'home', params: { page: n }}) 56 | break 57 | case keys.step: 58 | this.$store.commit('setStep', n) 59 | break 60 | default: 61 | break 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/router.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Deck from './views/Deck.vue' 4 | 5 | Vue.use(Router) 6 | 7 | export default new Router({ 8 | mode: 'hash', 9 | base: process.env.BASE_URL, 10 | routes: [ 11 | { 12 | path: '/:page?', 13 | name: 'home', 14 | component: Deck, 15 | }, 16 | ], 17 | }) 18 | -------------------------------------------------------------------------------- /src/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | export default new Vuex.Store({ 7 | state: { 8 | currentPage: 1, 9 | mode: 'normal', 10 | steps: 0, 11 | step: 0, 12 | }, 13 | mutations: { 14 | toggleMode(state, next) { 15 | state.mode = state.mode === next 16 | ? 'normal' 17 | : next 18 | }, 19 | setCurrentPage(state, page) { 20 | state.currentPage = page 21 | }, 22 | setSteps(state, steps) { 23 | state.steps = steps 24 | }, 25 | setStep(state, step) { 26 | state.step = step 27 | }, 28 | increaseStep(state) { 29 | state.step += 1 30 | }, 31 | decreaseStep(state) { 32 | state.step -= 1 33 | } 34 | }, 35 | }) 36 | -------------------------------------------------------------------------------- /src/styles/components/transitions.css: -------------------------------------------------------------------------------- 1 | /* Slide Transition 2 | ---------------------------------------------------------------------------- */ 3 | .slide-right-enter-active { 4 | transition: all .3s ease-out; 5 | } 6 | .slide-right-leave-active { 7 | transition: all .3s ease-out; 8 | } 9 | .slide-right-enter { 10 | opacity: .5; 11 | transform: scale(.5) translateX(-100%) translateY(50%); 12 | } 13 | .slide-right-leave-to { 14 | opacity: .5; 15 | transform: scale(.5) translateX(100%) translateY(50%); 16 | } 17 | 18 | 19 | .slide-left-enter-active { 20 | transition: all .3s ease-out; 21 | } 22 | .slide-left-leave-active { 23 | transition: all .3s ease-out; 24 | } 25 | .slide-left-enter { 26 | opacity: .5; 27 | transform: scale(.5) translateX(100%) translateY(50%); 28 | } 29 | .slide-left-leave-to { 30 | opacity: .5; 31 | transform: scale(.5) translateX(-100%) translateY(50%); 32 | } 33 | 34 | /* Fade Transition 35 | ---------------------------------------------------------------------------- */ 36 | .fade-enter-active, 37 | .fade-leave-active { 38 | transition: opacity .15s; 39 | } 40 | 41 | .fade-enter, 42 | .fade-leave-to { 43 | opacity: 0; 44 | } 45 | -------------------------------------------------------------------------------- /src/styles/main.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss/base"; 2 | 3 | /* Add custom base styles here... */ 4 | 5 | @import "tailwindcss/components"; 6 | 7 | /* purgecss start ignore */ 8 | @import "./components/transitions.css"; 9 | @import "./prism-atom-dark.css"; 10 | /* purgecss end ignore */ 11 | 12 | @import "tailwindcss/utilities"; 13 | @import "./utilities/transitions.css"; 14 | -------------------------------------------------------------------------------- /src/styles/prism-atom-dark.css: -------------------------------------------------------------------------------- 1 | /** 2 | * atom-dark theme for `prism.js` 3 | * Based on Atom's `atom-dark` theme: https://github.com/atom/atom-dark-syntax 4 | * @author Joe Gibson (@gibsjose) 5 | */ 6 | 7 | code[class*="language-"], 8 | pre[class*="language-"] { 9 | /*color: #c5c8c6;*/ 10 | /*text-shadow: 0 1px rgba(0, 0, 0, 0.3);*/ 11 | /*font-family: Inconsolata, Monaco, Consolas, 'Courier New', Courier, monospace;*/ 12 | direction: ltr; 13 | text-align: left; 14 | white-space: pre; 15 | word-spacing: normal; 16 | word-break: normal; 17 | line-height: 1.5; 18 | 19 | -moz-tab-size: 4; 20 | -o-tab-size: 4; 21 | tab-size: 4; 22 | 23 | -webkit-hyphens: none; 24 | -moz-hyphens: none; 25 | -ms-hyphens: none; 26 | hyphens: none; 27 | } 28 | 29 | /* Code blocks */ 30 | pre[class*="language-"] { 31 | @apply p-4; 32 | /*padding: 1em;*/ 33 | /*margin: .5em 0;*/ 34 | overflow: auto; 35 | border-radius: 0.3em; 36 | } 37 | 38 | /* Inline code */ 39 | :not(pre) > code[class*="language-"] { 40 | padding: .1em; 41 | border-radius: .3em; 42 | } 43 | 44 | .token.comment, 45 | .token.prolog, 46 | .token.doctype, 47 | .token.cdata { 48 | @apply text-gray-500; 49 | /*color: #7C7C7C;*/ 50 | } 51 | 52 | .token.punctuation { 53 | color: #c5c8c6; 54 | } 55 | 56 | .namespace { 57 | opacity: .7; 58 | } 59 | 60 | .token.property, 61 | .token.keyword, 62 | .token.tag { 63 | color: #96CBFE; 64 | } 65 | 66 | .token.class-name { 67 | color: #FFFFB6; 68 | text-decoration: underline; 69 | } 70 | 71 | .token.boolean, 72 | .token.constant { 73 | color: #99CC99; 74 | } 75 | 76 | .token.symbol, 77 | .token.deleted { 78 | color: #f92672; 79 | } 80 | 81 | .token.number { 82 | color: #FF73FD; 83 | } 84 | 85 | .token.selector, 86 | .token.attr-name, 87 | .token.string, 88 | .token.char, 89 | .token.builtin, 90 | .token.inserted { 91 | color: #A8FF60; 92 | } 93 | 94 | .token.variable { 95 | color: #C6C5FE; 96 | } 97 | 98 | .token.operator { 99 | color: #EDEDED; 100 | } 101 | 102 | .token.entity { 103 | color: #FFFFB6; 104 | /* text-decoration: underline; */ 105 | } 106 | 107 | .token.url { 108 | color: #96CBFE; 109 | } 110 | 111 | .language-css .token.string, 112 | .style .token.string { 113 | color: #87C38A; 114 | } 115 | 116 | .token.atrule, 117 | .token.attr-value { 118 | color: #F9EE98; 119 | } 120 | 121 | .token.function { 122 | color: #DAD085; 123 | } 124 | 125 | .token.regex { 126 | color: #E9C062; 127 | } 128 | 129 | .token.important { 130 | color: #fd971f; 131 | } 132 | 133 | .token.important, 134 | .token.bold { 135 | font-weight: bold; 136 | } 137 | .token.italic { 138 | font-style: italic; 139 | } 140 | 141 | .token.entity { 142 | cursor: help; 143 | } 144 | 145 | :not(pre) > code[class*="language-"], 146 | pre[class*="language-"] { 147 | background: transparent; 148 | } 149 | 150 | /*Custom overrides*/ 151 | .token.atrule, .token.atrule .token.number { 152 | color: #fff; 153 | } 154 | .token.atrule .token.rule { 155 | color: #f9ee98; 156 | } 157 | .token.function { 158 | color: #f9ee98; 159 | } 160 | .language-css .token.string, .style .token.string { 161 | color: #a8ff60; 162 | } 163 | 164 | /*New overrides*/ 165 | code[class*="language-"], pre[class*="language-"] { 166 | @apply subpixel-antialiased !important; 167 | @apply text-code-white !important; 168 | @apply scrolling-touch !important; 169 | } 170 | .token.comment { 171 | @apply text-gray-500 !important; 172 | } 173 | .token.atrule { 174 | @apply text-code-white !important; 175 | } 176 | .token.atrule > .token.property { 177 | @apply text-code-white !important; 178 | } 179 | .token.atrule > .token.property + .token.punctuation { 180 | @apply text-code-white !important; 181 | } 182 | .token.atrule > .token.property + .token.punctuation + .token.number + .token.unit { 183 | @apply text-code-white !important; 184 | } 185 | .token.atrule > .token.number { 186 | @apply text-code-white !important; 187 | } 188 | .token.atrule > .token.unit { 189 | @apply text-code-white !important; 190 | } 191 | .token.function { 192 | @apply text-code-blue !important; 193 | } 194 | .token.number { 195 | @apply text-code-red !important; 196 | } 197 | .token.unit { 198 | @apply text-code-red !important; 199 | } 200 | .token.punctuation { 201 | @apply text-code-blue !important; 202 | } 203 | .token.hexcode { 204 | @apply text-code-blue !important; 205 | } 206 | .token.tag { 207 | @apply text-code-red !important; 208 | } 209 | .token.attr-name { 210 | @apply text-code-yellow !important; 211 | } 212 | .token.attr-value { 213 | @apply text-code-green !important; 214 | } 215 | .token.string { 216 | @apply text-code-green !important; 217 | } 218 | .token.url { 219 | @apply text-code-green !important; 220 | } 221 | .token.selector { 222 | @apply text-code-yellow !important; 223 | } 224 | .token.property { 225 | @apply text-code-yellow !important; 226 | } 227 | .token.rule { 228 | @apply text-code-purple !important; 229 | } 230 | .token.important { 231 | font-weight: inherit !important; 232 | @apply text-code-purple !important; 233 | } 234 | 235 | code.language-js, pre.language-js { 236 | & .token.operator { 237 | @apply text-code-blue !important; 238 | } 239 | & .token.punctuation { 240 | @apply text-code-white !important; 241 | } 242 | & .token.boolean { 243 | @apply text-code-red !important; 244 | } 245 | & .token.keyword { 246 | @apply text-code-white !important; 247 | } 248 | & .token.regex { 249 | @apply text-code-yellow !important; 250 | } 251 | } 252 | 253 | code.language-bash, pre.language-bash { 254 | & .token.function { 255 | @apply text-code-white !important; 256 | } 257 | } 258 | 259 | code.language-diff, pre.language-diff { 260 | @apply text-gray-400 !important; 261 | & .token.deleted { 262 | @apply text-code-red !important; 263 | } 264 | & .token.inserted { 265 | @apply text-code-green !important; 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /src/styles/utilities/transitions.css: -------------------------------------------------------------------------------- 1 | .origin-left { 2 | transform-origin: left; 3 | } 4 | .origin-right { 5 | transform-origin: right; 6 | } 7 | .transition-all { 8 | transition-property: all; 9 | } 10 | .transition-transform { 11 | transition-property: transform; 12 | } 13 | .transition-fastest { 14 | transition-duration: 50ms; 15 | } 16 | .transition-faster { 17 | transition-duration: 100ms; 18 | } 19 | .transition-fast { 20 | transition-duration: 150ms; 21 | } 22 | .transition-medium { 23 | transition-duration: 200ms; 24 | } 25 | .transition-normal { 26 | transition-duration: 300ms; 27 | } 28 | .transition-slow { 29 | transition-duration: 1000ms; 30 | } 31 | .ease-out-quad { 32 | transition-timing-function: cubic-bezier(.25, .46, .45, .94); 33 | } 34 | .ease-in-quad { 35 | transition-timing-function: cubic-bezier(.55, .085, .68, .53); 36 | } 37 | .scale-0 { 38 | transform: scale(0); 39 | } 40 | .scale-70 { 41 | transform: scale(.7); 42 | } 43 | .scale-100 { 44 | transform: scale(1); 45 | } 46 | 47 | .grayscale-100 { 48 | filter: grayscale(1); 49 | } 50 | .hover\:grayscale-0:hover { 51 | filter: grayscale(0); 52 | } 53 | -------------------------------------------------------------------------------- /src/views/Deck.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 73 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | important: false, 3 | theme: require(process.env.__TAILWIND_THEME_CONFIG_PATH__), 4 | plugins: [ 5 | function({ addBase, config }) { 6 | addBase({ 7 | 'h1': { fontSize: config('theme.fontSize.6xl') }, 8 | 'h2': { fontSize: config('theme.fontSize.5xl') }, 9 | 'h3': { fontSize: config('theme.fontSize.4xl') }, 10 | 'h4': { fontSize: config('theme.fontSize.3xl') }, 11 | 'h5': { fontSize: config('theme.fontSize.2xl') }, 12 | 'h6': { fontSize: config('theme.fontSize.xl') }, 13 | }) 14 | }, 15 | require('tailwindcss-typography')({ 16 | componentPrefix: '', 17 | }), 18 | ], 19 | } 20 | -------------------------------------------------------------------------------- /tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | jest: true 6 | }, 7 | extends: [ 8 | 'plugin:vue/essential', 9 | 'eslint:recommended' 10 | ], 11 | rules: { 12 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 13 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 14 | 'vue/no-unused-components': 1 15 | }, 16 | parserOptions: { 17 | parser: 'babel-eslint' 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /tests/unit/example.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils'; 2 | import HelloWorld from '@/components/HelloWorld.vue'; 3 | 4 | describe('HelloWorld.vue', () => { 5 | it('renders props.msg when passed', () => { 6 | const msg = 'new message'; 7 | const wrapper = shallowMount(HelloWorld, { 8 | propsData: { msg }, 9 | }); 10 | expect(wrapper.text()).toMatch(msg); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /theme.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extend: { 3 | colors: { 4 | theme: { 5 | text: '#fff', 6 | background: '#011627', 7 | dot: '#828A92', 8 | }, 9 | code: { 10 | green: '#b5f4a5', 11 | yellow: '#ffe484', 12 | purple: '#d9a9ff', 13 | red: '#ff8383', 14 | blue: '#93ddfd', 15 | white: '#fff', 16 | }, 17 | }, 18 | }, 19 | // https://github.com/benface/tailwindcss-typography 20 | textStyles: theme => ({ 21 | heading: { 22 | output: false, 23 | color: '#6AD798', 24 | fontWeight: theme('fontWeight.bold'), 25 | lineHeight: theme('lineHeight.tight'), 26 | }, 27 | h1: { extends: 'heading', fontSize: theme('fontSize.6xl') }, 28 | h2: { extends: 'heading', fontSize: theme('fontSize.5xl') }, 29 | h3: { extends: 'heading', fontSize: theme('fontSize.4xl') }, 30 | link: { 31 | fontWeight: theme('fontWeight.bold'), 32 | color: theme('colors.blue.600'), 33 | '&:hover': { 34 | color: theme('colors.blue.500'), 35 | textDecoration: 'underline', 36 | }, 37 | }, 38 | markdown: { 39 | backgroundColor: theme('colors.theme.background'), 40 | display: 'flex', 41 | flexDirection: 'column', 42 | alignItems: 'center', 43 | justifyContent: 'center', 44 | overflow: 'hidden', 45 | fontWeight: theme('fontWeight.normal'), 46 | fontSize: '32px', 47 | color: theme('colors.theme.text'), 48 | lineHeight: theme('lineHeight.relaxed'), 49 | '> * + *': { 50 | marginTop: '1.5rem', 51 | }, 52 | '> h1': { extends: 'h1' }, 53 | '> h2': { extends: 'h2' }, 54 | '> h3': { extends: 'h3' }, 55 | 'ul': { 56 | paddingLeft: '1.5rem', 57 | listStyleType: 'disc', 58 | }, 59 | 'ol': { 60 | paddingLeft: '1.5rem', 61 | listStyleType: 'decimal', 62 | }, 63 | 'a': { 64 | extends: 'link', 65 | }, 66 | 'b, strong': { 67 | fontWeight: theme('fontWeight.bold'), 68 | }, 69 | 'i, em': { 70 | fontStyle: 'italic', 71 | }, 72 | 'pre[class*=language-]': { 73 | padding: '1rem', 74 | fontSize: theme('fontSize.base'), 75 | fontFamily: theme('fontFamily.mono'), 76 | fontWeight: theme('fontWeight.normal'), 77 | lineHeight: theme('lineHeight.normal'), 78 | backgroundColor: theme('colors.gray.800'), 79 | color: theme('colors.gray.200'), 80 | whitespace: 'nowrap', 81 | scrollbarWidth: 'none', 82 | }, 83 | 'pre[class*=language-]::-webkit-scrollbar': { 84 | display: 'none', 85 | }, 86 | 'table': { 87 | width: '50%', 88 | textAlign: 'left', 89 | borderCollapse: 'collapse', 90 | overflow: 'scroll-x', 91 | }, 92 | 'table th, table td': { 93 | minWidth: '200px', 94 | }, 95 | 'table th': { 96 | fontSize: theme('fontSize.lg'), 97 | fontWeight: theme('fontWeight.semibold'), 98 | padding: '.5rem', 99 | color: theme('colors.gray.900'), 100 | backgroundColor: theme('colors.gray.300'), 101 | }, 102 | 'table td': { 103 | fontSize: theme('fontSize.lg'), 104 | padding: '.5rem', 105 | borderWidth: '1px', 106 | borderColor: theme('colors.gray.400'), 107 | } 108 | }, 109 | }), 110 | } 111 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | configureWebpack: { 3 | module: { 4 | rules: [ 5 | { 6 | test: /.mdx?$/, 7 | use: [ 8 | 'babel-loader', 9 | { 10 | loader: require.resolve('./markdown-loader') 11 | }, 12 | '@mdx-js/vue-loader' 13 | ] 14 | } 15 | ] 16 | }, 17 | }, 18 | chainWebpack: config => { 19 | config.resolve.alias 20 | .set('@deck', process.env.SRC_DECK) 21 | } 22 | } 23 | --------------------------------------------------------------------------------