Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable auto-scaling features in theme metadata explicitly #81

Merged
merged 6 commits into from
Apr 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased]

### Breaking

- Change auto-scaling features to require enabling by `@auto-scaling` metadata of theme CSS explicitly ([#81](https://github.com/marp-team/marp-core/pull/81))

### Changed

- Upgrade Marpit to [v0.9.0](https://github.com/marp-team/marpit/releases/v0.9.0) ([#80](https://github.com/marp-team/marp-core/pull/80))
Expand Down
42 changes: 32 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,29 @@ $$
</tbody>
</table>

### Auto scaling features
### Auto-scaling features

Auto scaling is available only if enabled [Marpit's `inlineSVG` mode](https://github.com/marp-team/marpit#inline-svg-slide-experimental). You have to run [`Marp.ready()`](#marpready) on browser context.
Auto-scaling is available only if enabled [Marpit's `inlineSVG` mode](https://github.com/marp-team/marpit#inline-svg-slide-experimental) and defined `@auto-scaling` meta data in an using theme CSS. In addition, you have to run [`Marp.ready()`](#marpready) on browser context.

```css
/*
* @theme enable-all-auto-scaling
* @auto-scaling true
*/
```

Marp Core's scaling features will be realized by manipulating the original DOM to use inline SVG. So the theme author must take care of updated DOM in styling. Refer to [the source code of offical themes][themes].

`@auto-scaling` meta can also pick the favorite features to enable by using keyword(s).

```css
/*
* @theme enable-auto-scaling-for-fitting-header-and-math
* @auto-scaling fittingHeader,math
*/
```

> :warning: In the math block and the code block, Marp Core won't detect whether they actually protrude from the slide. It might not work scaling correctly when there are many elements in a slide.

#### Fitting header

Expand All @@ -135,27 +155,29 @@ When the headings contains `<!-- fit -->` comment, the size of headings will res

This syntax is similar to [Deckset's `[fit]` keyword](https://docs.decksetapp.com/English.lproj/Formatting/01-headings.html), but we use HTML comment to hide a fit keyword on Markdown rendered as document.

> :information_source: `@auto-scaling fittingHeader` is a keyword of the `@auto-scaling` meta to enable fitting header.

#### Math block

We can scale-down the viewing size of math block (surrounded by `$$`) to fit a slide automatically.

| Traditional rendering | Auto scaling |
| Traditional rendering | Auto-scaling |
| :----------------------------------------------: | :-------------------------------------: |
| ![Traditional rendering](https://bit.ly/2NXoHuW) | ![Auto scaling](https://bit.ly/2M6LyCk) |
| ![Traditional rendering](https://bit.ly/2NXoHuW) | ![Auto-scaling](https://bit.ly/2M6LyCk) |

#### Code block (Only for `default` and `gaia` theme)
> :information_source: `@auto-scaling math` is a keyword of the `@auto-scaling` meta to enable math block scaling.

#### Code block

Several themes also can scale-down the viewing size of the code block to fit a slide.

| Traditional rendering | Auto scaling |
| Traditional rendering | Auto-scaling |
| :----------------------------------------------: | :-------------------------------------: |
| ![Traditional rendering](https://bit.ly/2LyEnmi) | ![Auto scaling](https://bit.ly/2N4yWQZ) |
| ![Traditional rendering](https://bit.ly/2LyEnmi) | ![Auto-scaling](https://bit.ly/2N4yWQZ) |

These features means that the contents on a slide are not cropped, and not shown unnecessary scrollbars in code.

> :information_source: `uncover` theme has disabled scaling for code block because we use elastic style that has not compatible with it.

> :warning: We won't detect whether the math and code block actually protrudes from the slide. It might not work scaling correctly when there are many elements in a slide.
> :information_source: `@auto-scaling code` is a keyword of the `@auto-scaling` meta to enable code block scaling. `uncover` theme has disabled code block scaling because we use elastic style that has not compatible with it.

## Constructor options

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
"typescript": "^3.3.3333"
},
"dependencies": {
"@marp-team/marpit": "^0.9.0",
"@marp-team/marpit": "^0.9.1",
"@marp-team/marpit-svg-polyfill": "^0.3.0",
"emoji-regex": "^8.0.0",
"highlight.js": "^9.15.6",
Expand Down
111 changes: 59 additions & 52 deletions src/fitting/fitting.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import marpitPlugin from '@marp-team/marpit/lib/markdown/marpit_plugin'
import fittingCSS from './fitting.scss'
import { Marp } from '../marp'
import { getThemeMeta } from '../theme'

export const css = fittingCSS
export const attr = 'data-marp-fitting'
Expand All @@ -9,41 +10,35 @@ export const math = 'data-marp-fitting-math'
export const svgContentAttr = 'data-marp-fitting-svg-content'
export const svgContentWrapAttr = 'data-marp-fitting-svg-content-wrap'

export type ThemeResolver = () => string | undefined
const codeMatcher = /^(<pre[^>]*?><code[^>]*?>)([\s\S]*)(<\/code><\/pre>\n*)$/

function wrapTokensByFittingToken(token, tokens: any[]): any[] {
const open = new token('marp_fitting_open', 'span', 1)
open.attrSet(attr, 'plain')

return [open, ...tokens, new token('marp_fitting_close', 'span', -1)]
const isEnabledAutoScaling = (marp: Marp, key?: string): boolean => {
const meta = getThemeMeta(marp, 'auto-scaling') || ''
return !!(meta === 'true' || (key && meta.includes(key)))
}

// Wrap code block and fence renderer by fitting elements.
function fittingCode(md, themeResolver: ThemeResolver): void {
function fittingCode(md): void {
const { code_block, fence } = md.renderer.rules

const codeMatcher = /^(<pre[^>]*?><code[^>]*?>)([\s\S]*)(<\/code><\/pre>\n*)$/

const replacedRenderer = func => (...args) => {
const rendered: string = func(...args)

const { fittingCode } = md.marpit.themeSet.getThemeProp(
themeResolver()!,
'meta'
)
if (fittingCode === 'false') return rendered
if (isEnabledAutoScaling(md.marpit, 'code')) {
return rendered.replace(codeMatcher, (_, start, content, end) => {
if (md.marpit.options.inlineSVG) {
return [
`${start}<svg ${attr}="svg" ${code}><foreignObject>`,
`<span ${svgContentAttr}><span ${svgContentWrapAttr}>`,
content,
`</span></span></foreignObject></svg>${end}`,
].join('')
}
return `${start}<span ${attr}="plain">${content}</span>${end}`
})
}

return rendered.replace(codeMatcher, (_, start, content, end) => {
if (md.marpit.options.inlineSVG) {
return [
`${start}<svg ${attr}="svg" ${code}><foreignObject>`,
`<span ${svgContentAttr}><span ${svgContentWrapAttr}>`,
content,
`</span></span></foreignObject></svg>${end}`,
].join('')
}
return `${start}<span ${attr}="plain">${content}</span>${end}`
})
return rendered
}

md.renderer.rules.code_block = replacedRenderer(code_block)
Expand Down Expand Up @@ -71,10 +66,14 @@ function fittingHeader(md): void {
}

if (requireWrapping) {
token.children = wrapTokensByFittingToken(
state.Token,
token.children
)
const open = new state.Token('marp_fitting_open', 'span', 1)
open.attrSet(attr, 'plain')

token.children = [
open,
...token.children,
new state.Token('marp_fitting_close', 'span', -1),
]
}
} else if (token.type === 'heading_close') {
target = undefined
Expand All @@ -83,39 +82,47 @@ function fittingHeader(md): void {
}
})

if (md.marpit.options.inlineSVG) {
Object.assign(md.renderer.rules, {
marp_fitting_open: () =>
`<svg ${attr}="svg"><foreignObject><span ${svgContentAttr}>`,
marp_fitting_close: () => '</span></foreignObject></svg>',
})
}
md.renderer.rules.marp_fitting_open = () =>
isEnabledAutoScaling(md.marpit, 'fittingHeader')
? md.marpit.options.inlineSVG
? `<svg ${attr}="svg"><foreignObject><span ${svgContentAttr}>`
: `<span ${attr}="plain">`
: ''

md.renderer.rules.marp_fitting_close = () =>
isEnabledAutoScaling(md.marpit, 'fittingHeader')
? `</span>${md.marpit.options.inlineSVG ? '</foreignObject></svg>' : ''}`
: ''
}

function fittingMathBlock(md): void {
const { marp_math_block } = md.renderer.rules
if (!marp_math_block) return

const replacedRenderer = func => (...args) => {
md.renderer.rules.marp_math_block = (...args) => {
// Rendered math block is wrapped by `<p>` tag in math plugin
const katex: string = func(...args).slice(3, -4)

if (md.marpit.options.inlineSVG) {
return [
`<p><svg ${attr}="svg" ${math}><foreignObject>`,
`<span ${svgContentAttr}><span ${svgContentWrapAttr}>`,
katex,
`</span></span></foreignObject></svg></p>`,
].join('')
const rendered: string = marp_math_block(...args)

if (isEnabledAutoScaling(md.marpit, 'math')) {
const katex = rendered.slice(3, -4)

if (md.marpit.options.inlineSVG) {
return [
`<p><svg ${attr}="svg" ${math}><foreignObject>`,
`<span ${svgContentAttr}><span ${svgContentWrapAttr}>`,
katex,
`</span></span></foreignObject></svg></p>`,
].join('')
}
return `<p><span ${attr}="plain">${katex}</span></p>`
}
return `<p><span ${attr}="plain">${katex}</span></p>`
}

md.renderer.rules.marp_math_block = replacedRenderer(marp_math_block)
return rendered
}
}

export const markdown = marpitPlugin((md, themeResolver: ThemeResolver) => {
export const markdown = marpitPlugin(md => {
md.use(fittingHeader)
md.use(fittingCode, themeResolver)
md.use(fittingMathBlock)
.use(fittingCode)
.use(fittingMathBlock)
})
5 changes: 1 addition & 4 deletions src/marp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,7 @@ export class Marp extends Marpit {
md.use(htmlPlugin.markdown)
.use(emojiPlugin.markdown)
.use(mathPlugin.markdown, flag => (this.renderedMath = flag))
.use(
fittingPlugin.markdown,
() => (this.lastGlobalDirectives || {}).theme
)
.use(fittingPlugin.markdown)
}

highlighter(code: string, lang: string): string {
Expand Down
8 changes: 8 additions & 0 deletions src/theme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Marp from './marp'

export function getThemeMeta(marp: Marp, meta: string): string | undefined {
const { lastGlobalDirectives } = marp as any
const theme = lastGlobalDirectives ? lastGlobalDirectives.theme : undefined

return marp.themeSet.getThemeProp(theme, `meta.${meta}`)
}
2 changes: 1 addition & 1 deletion test/marp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ describe('Marp', () => {
expect($('pre').text()).toContain('CODE BLOCK')
})

it('does not wrap by svg when specified theme has fittingCode meta as false', () => {
it('does not wrap by svg when specified uncover theme', () => {
const instance = marp()
const theme = instance.themeSet.get('uncover')!
theme.meta.fittingCode = 'false'
Expand Down
27 changes: 24 additions & 3 deletions themes/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Marp-core built-in themes
# Marp Core built-in themes

We provide some nice official themes in marp-core. You can choose a favorite theme by using [Marpit `theme` directive](https://marpit.marp.app/directives?id=theme) in your Markdown.
We provide some nice official themes in Marp Core. You can choose a favorite theme by using [Marpit `theme` directive](https://marpit.marp.app/directives?id=theme) in your Markdown.

Screenshots were taken from the rendered result of [an example][example].

Expand All @@ -14,6 +14,8 @@ The all of built-in themes support `invert` class to use the inverted color sche
<!-- class: invert -->
```

---

## Default

[![](https://user-images.githubusercontent.com/3993388/48039490-53be1b80-e1b8-11e8-8179-0e6c11d285e2.png)][example]
Expand Down Expand Up @@ -101,4 +103,23 @@ Uncover theme has three design concepts: simple, minimal, and modern. It's inspi

### :warning: Restrictions

[Auto scaling for code block](https://github.com/marp-team/marp-core#auto-scaling-features) is disabled because uncover theme uses the elastic style that has not compatible with it.
[Auto-scaling for code block](https://github.com/marp-team/marp-core#auto-scaling-features) is disabled because uncover theme uses the elastic style that has not compatible with it.

---

## Metadata for additional features

Marp Core will recognize the metadata to be able to enable extra features whose side effect through manipulation to rendered DOM structure.

In other words, the enabled feature requires taking care of manipulated DOM when styling.

### `@auto-scaling`

Enable [auto-scaling features](https://github.com/marp-team/marp-core#auto-scaling-features).

- `true`: Enable all features.
- `fittingHeader`: Enable fitting header.
- `math`: Enable scaling for math block.
- `code`: Enable scaling for code block.

Through separating by comma, it can select multiple keywords for individual features. (e.g. `@auto-scaling fittingHeader,math`)
3 changes: 3 additions & 0 deletions themes/default.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
* Marp default theme.
*
* @theme default
* @author Yuki Hattori
*
* @auto-scaling true
*/

@import '~github-markdown-css';
Expand Down
2 changes: 2 additions & 0 deletions themes/gaia.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
*
* @theme gaia
* @author Yuki Hattori
*
* @auto-scaling true
*/

$color-light: #fff8e1;
Expand Down
3 changes: 2 additions & 1 deletion themes/uncover.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
*
* @theme uncover
* @author Yuki Hattori
* @fittingCode false
*
* @auto-scaling fittingHeader,math
*/

@mixin color-scheme($bg: #fdfcff, $text: #202228, $highlight: #009dd5) {
Expand Down
9 changes: 5 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -302,13 +302,14 @@
dependencies:
detect-browser "^4.1.0"

"@marp-team/marpit@^0.9.0":
version "0.9.0"
resolved "https://registry.yarnpkg.com/@marp-team/marpit/-/marpit-0.9.0.tgz#5a12f00a97832907806af81933d8d2f5e25cb4e6"
integrity sha512-nPtIXu2leY/EDKsjvu3g4tsa98yRAXpM1w2bpUUKLtPC0wirdhxIJWGjW8W24Hm0+c+F/f3jibE7B15fTCVweA==
"@marp-team/marpit@^0.9.1":
version "0.9.1"
resolved "https://registry.yarnpkg.com/@marp-team/marpit/-/marpit-0.9.1.tgz#6d25110b370f079378a0ef153776b3aa3d891163"
integrity sha512-+60TACjL4M/dGP9KG5WJse1iLbaD4RAMkBpCq7qfwm7JU730WjM6zdTfSXJ6U2o6U/yoJsaDq5UYJ4MNGMwRMA==
dependencies:
color-string "^1.5.3"
js-yaml "^3.13.0"
lodash.get "^4.4.2"
lodash.kebabcase "^4.1.1"
markdown-it "^8.4.2"
markdown-it-front-matter "^0.1.2"
Expand Down