mdeck 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/assets/base.css +24 -0
- package/docs/reference/authoring.md +13 -0
- package/docs/reference/extensions.md +3 -1
- package/package.json +1 -1
- package/src/build/themeCheck.js +12 -2
- package/src/cli/themes.js +9 -3
- package/src/runtime/markedSetup.js +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.2.0 — 2026-10-07
|
|
4
|
+
|
|
5
|
+
- Pictures written in the text fit on the slide in every theme: a paragraph of nothing but pictures shrinks into the room below the heading instead of running off the slide, keeping its proportions (small pictures keep their size), and several pictures in one paragraph sit side by side. Such paragraphs have the class `pictures`, for themes that set them their own way; a picture in a sentence is left alone.
|
|
6
|
+
- Tables have room below them in every theme, so a sentence after a table no longer sits right against it.
|
|
7
|
+
- `mdeck themes build` also renders an icon for each theme: its title slide at 96×54 (drawn at twice that, about 3 KB), listed as `icon` in `catalogue.json` and `looks.json`. The theme repository's gallery shows it in its list instead of the palette's colours, which said little about a theme.
|
|
8
|
+
|
|
3
9
|
## 3.1.0 — 2026-10-07
|
|
4
10
|
|
|
5
11
|
- Packs are gone: every theme and palette is installed on its own. `mdeck themes install duet` installs the theme with its default palette (`cobalt`), and `mdeck palettes install solarized` a palette; both have `search`, `remove`, `update` and `list`. A palette made for one theme (FHNW's `brand`) brings that theme along and goes with it. A palette an installed theme uses by default is not removed while that theme is there. Installs go into `themes/<id>/` and `palettes/<id>/` of the extensions folder, so a theme and a palette may share a name.
|
package/assets/base.css
CHANGED
|
@@ -162,11 +162,35 @@ deck-stage[data-ink-hidden] .slide-ink { display: none; }
|
|
|
162
162
|
/* ── Code blocks ─────────────────────────────────────────────────────────── */
|
|
163
163
|
.code-block { white-space: pre-wrap; overflow-x: hidden; }
|
|
164
164
|
|
|
165
|
+
/* ── Pictures written in the text ───────────────────────────────────────── */
|
|
166
|
+
/* A paragraph of nothing but pictures (marked in markedSetup.js) fits into
|
|
167
|
+
the room the slide has left: a large picture shrinks, keeping its
|
|
168
|
+
proportions, and never runs off the slide; a small one keeps its size.
|
|
169
|
+
Several sit side by side. Themes may set them differently (a grid, cropped
|
|
170
|
+
to fill); a picture in a sentence is left alone. */
|
|
171
|
+
.slide-body p.pictures {
|
|
172
|
+
display: flex;
|
|
173
|
+
align-items: center;
|
|
174
|
+
gap: 16px;
|
|
175
|
+
flex: 0 1 auto;
|
|
176
|
+
min-height: 0;
|
|
177
|
+
}
|
|
178
|
+
.slide-body p.pictures > :is(img, .themed-picture) {
|
|
179
|
+
flex: 0 1 auto;
|
|
180
|
+
min-width: 0;
|
|
181
|
+
min-height: 0;
|
|
182
|
+
max-width: 100%;
|
|
183
|
+
max-height: 100%;
|
|
184
|
+
object-fit: contain;
|
|
185
|
+
}
|
|
186
|
+
|
|
165
187
|
/* ── Tables ──────────────────────────────────────────────────────────────── */
|
|
166
188
|
.slide-body table {
|
|
167
189
|
border-collapse: collapse;
|
|
168
190
|
width: 100%;
|
|
169
191
|
font-size: var(--fs-body);
|
|
192
|
+
/* Room before what follows, such as a sentence about the numbers. */
|
|
193
|
+
margin-bottom: calc(var(--gap-item, 24px) * 1.5);
|
|
170
194
|
}
|
|
171
195
|
.slide-body th,
|
|
172
196
|
.slide-body td {
|
|
@@ -594,6 +594,19 @@ Reference images with standard Markdown syntax or the `image:` frontmatter field
|
|
|
594
594
|

|
|
595
595
|
```
|
|
596
596
|
|
|
597
|
+
A paragraph of nothing but pictures fits into the room the slide has left: a
|
|
598
|
+
large picture shrinks, keeping its proportions, and a small one keeps its
|
|
599
|
+
size. Several pictures in one paragraph sit side by side:
|
|
600
|
+
|
|
601
|
+
```markdown
|
|
602
|
+
# Three mornings
|
|
603
|
+
|
|
604
|
+
  
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
A picture inside a sentence stays in the sentence. Themes may set picture
|
|
608
|
+
paragraphs their own way (Lightbox fills a grid edge to edge).
|
|
609
|
+
|
|
597
610
|
A local SVG whose source contains `var(--…)` is drawn inline instead of as an
|
|
598
611
|
`<img>`, both in slide text (`` or `<img src>`) and as a
|
|
599
612
|
layout's `image` (`title`, `chapter`, `image-text`, `full-bleed-image`, and
|
|
@@ -246,7 +246,9 @@ touch files changed since then unless given `--force`.
|
|
|
246
246
|
|
|
247
247
|
The repository serves `catalogue.json` (`{ schema: 2, themes: [...],
|
|
248
248
|
palettes: [...] }`, each with its id, version, default palette or theme,
|
|
249
|
-
`url`, `sha256`, `preview`,
|
|
249
|
+
`url`, `sha256`, `preview`, for a theme an `icon` (its title slide at
|
|
250
|
+
96×54, drawn at twice that for sharp screens), and `builtIn` for those that
|
|
251
|
+
come with mdeck) and
|
|
250
252
|
one JSON file per version holding the text of its files.
|
|
251
253
|
`mdeck themes build <repository> -o <out> [--check]` writes both from a
|
|
252
254
|
folder with `themes/` and `palettes/`; `MDECK_THEMES_URL` points mdeck at
|
package/package.json
CHANGED
package/src/build/themeCheck.js
CHANGED
|
@@ -147,15 +147,25 @@ export function checkLooks(looks, { extensions = {} } = {}) {
|
|
|
147
147
|
})
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
// The size of a theme's icon in lists, in CSS pixels; drawn at twice that
|
|
151
|
+
// for sharp screens.
|
|
152
|
+
export const ICON = { width: 96, height: 54, scale: 2 }
|
|
153
|
+
|
|
150
154
|
// Pictures of looks on the sample deck, as WebP: { file, theme, palette,
|
|
151
|
-
// slide } each, slide counting from 1.
|
|
155
|
+
// slide, icon } each, slide counting from 1. `icon`, if given, is a second
|
|
156
|
+
// file with the same slide at icon size: small enough for a list, and still
|
|
157
|
+
// showing the theme's type, colours and shapes.
|
|
152
158
|
export function renderPreviews(shots, { extensions = {}, width = 960, height = 540 } = {}) {
|
|
153
159
|
return withSampleDeck({ extensions }, async ({ open }) => {
|
|
154
|
-
for (const { file, theme, palette = '', slide = 1 } of shots) {
|
|
160
|
+
for (const { file, theme, palette = '', slide = 1, icon = null } of shots) {
|
|
155
161
|
const page = await open({ theme, palette })
|
|
156
162
|
if (slide > 1) { await page.evaluate(`document.querySelector('deck-stage').goTo(${slide - 1})`); await settle(page) }
|
|
157
163
|
mkdirSync(resolve(file, '..'), { recursive: true })
|
|
158
164
|
writeFileSync(file, await page.screenshot({ width, height, format: 'webp' }))
|
|
165
|
+
if (icon) {
|
|
166
|
+
mkdirSync(resolve(icon, '..'), { recursive: true })
|
|
167
|
+
writeFileSync(icon, await page.screenshot({ ...ICON, format: 'webp' }))
|
|
168
|
+
}
|
|
159
169
|
await page.close()
|
|
160
170
|
}
|
|
161
171
|
})
|
package/src/cli/themes.js
CHANGED
|
@@ -25,11 +25,14 @@ const usage = kind => `search [words] | install <${kind}> | remove <${kind}> | u
|
|
|
25
25
|
function looksOf(registry, catalogue) {
|
|
26
26
|
const builtIn = new Set([...catalogue.themes, ...catalogue.palettes].filter(entry => entry.builtIn).map(entry => entry.id + (catalogue.themes.includes(entry) ? ':t' : ':p')))
|
|
27
27
|
const palettes = manifestsOf(registry, 'palette')
|
|
28
|
+
// Each theme's icon for the gallery's list (see renderPreviews).
|
|
29
|
+
const icons = new Map(catalogue.themes.map(entry => [entry.id, entry.icon]))
|
|
28
30
|
const colours = tokens => Object.fromEntries(['--bg', '--surface', '--ink', '--accent', '--accent-2'].map(key => [key, tokens?.[key] ?? null]))
|
|
29
31
|
return {
|
|
30
32
|
mdeck: VERSION,
|
|
31
33
|
themes: Object.values(manifestsOf(registry, 'theme')).map(theme => ({ id: theme.id, title: theme.title, description: theme.description ?? '', builtIn: builtIn.has(theme.id + ':t'),
|
|
32
|
-
palette: theme.palette, appearance: theme.appearance ?? 'light', palettes: palettesFor(theme, palettes).map(p => p.id)
|
|
34
|
+
palette: theme.palette, appearance: theme.appearance ?? 'light', palettes: palettesFor(theme, palettes).map(p => p.id),
|
|
35
|
+
...(icons.get(theme.id) ? { icon: icons.get(theme.id) } : {}) })),
|
|
33
36
|
palettes: Object.values(palettes).map(palette => ({ id: palette.id, title: palette.title, description: palette.description ?? '', builtIn: builtIn.has(palette.id + ':p'),
|
|
34
37
|
...(palette.theme ? { theme: palette.theme } : {}), light: colours(palette.light), dark: colours(palette.dark) })),
|
|
35
38
|
}
|
|
@@ -148,8 +151,11 @@ export async function runPackages({ kind, positionals, flag, output, ui: { ok, e
|
|
|
148
151
|
ok(`Checked ${catalogue.themes.length} themes and ${catalogue.palettes.length} palettes, light and dark, on every kind of slide`)
|
|
149
152
|
}
|
|
150
153
|
// Themes on the title slide, palettes on the chapter slide (the third).
|
|
151
|
-
|
|
152
|
-
|
|
154
|
+
// Themes also get an icon of their title slide for lists; a palette's
|
|
155
|
+
// colours are its icon.
|
|
156
|
+
await renderPreviews(looks.map(look => ({ file: resolve(out, 'previews', `${look.key}.webp`), theme: look.theme, palette: look.palette ?? '', slide: look.palette ? 3 : 1,
|
|
157
|
+
...(look.palette ? {} : { icon: resolve(out, 'previews', `${look.key}-icon.webp`) }) })), { extensions })
|
|
158
|
+
for (const entry of catalogue.themes) Object.assign(entry, { preview: `previews/themes/${entry.id}.webp`, icon: `previews/themes/${entry.id}-icon.webp` })
|
|
153
159
|
for (const entry of catalogue.palettes) entry.preview = `previews/palettes/${entry.id}.webp`
|
|
154
160
|
writeFileSync(resolve(out, 'catalogue.json'), JSON.stringify(catalogue, null, 2) + '\n')
|
|
155
161
|
// For the repository's theme browser: the sample deck with every theme
|
|
@@ -143,5 +143,12 @@ marked.use({
|
|
|
143
143
|
.replace(/>/g, '>')
|
|
144
144
|
return `<codeblock lang="${lang}"${attrs ? ' ' + attrs : ''}>${encoded}</codeblock>`
|
|
145
145
|
},
|
|
146
|
+
// A paragraph of nothing but pictures (one, or several side by side) is
|
|
147
|
+
// marked, so themes can fit it into the slide or set it as a row; a
|
|
148
|
+
// sentence with an icon in it stays a sentence.
|
|
149
|
+
paragraph({ tokens }) {
|
|
150
|
+
const pictures = tokens.some(t => t.type === 'image') && tokens.every(t => t.type === 'image' || t.type === 'br' || (t.type === 'text' && !t.raw.trim()))
|
|
151
|
+
return `<p${pictures ? ' class="pictures"' : ''}>${this.parser.parseInline(tokens)}</p>\n`
|
|
152
|
+
},
|
|
146
153
|
},
|
|
147
154
|
})
|