mdeck 3.9.0 → 3.10.1
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 +8 -0
- package/assets/extensions/themes/glass/extension.toml +9 -1
- package/assets/extensions/themes/glass/styles.css +4 -4
- package/docs/reference/authoring.md +4 -1
- package/docs/reference/extensions.md +1 -1
- package/docs/reference/themes.md +2 -1
- package/docs/site/content/theme-authoring.md +5 -2
- package/package.json +1 -1
- package/src/editor/components/DeckSettings.jsx +4 -4
- package/src/editor/components/ExtensionForms.jsx +1 -1
- package/src/extensions/params.js +28 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.10.1 — 2026-10-11
|
|
4
|
+
|
|
5
|
+
- Theme settings can be text: `type = "text"` for words a theme shows, such as Terminal's prompt. Authors write them as they should appear (`prompt: "~/demo $ "`), spaces included, and mdeck quotes them for the stylesheet; the editor shows a text box with the theme's own words. Before, such words could only be fixed in the theme.
|
|
6
|
+
|
|
7
|
+
## 3.10.0 — 2026-10-11
|
|
8
|
+
|
|
9
|
+
- Glass has a `motion` setting: `motion: 0` holds its light still, for recordings and calm rooms. The guide for theme authors names the settings themes share: `motion` for what moves while presenting and `texture` for grain, paper and scanlines.
|
|
10
|
+
|
|
3
11
|
## 3.9.0 — 2026-10-11
|
|
4
12
|
|
|
5
13
|
- Theme parameters say what kind of value they take: `type = "size"` (pixels, with `min`, `max` and `step`), `"number"`, `"switch"` (1 or 0), `"choice"` (one of `choices`) or `"font"`. The deck settings show a slider, a box to tick or a list instead of a text box; a deck value that does not fit is left out, so the theme's own value stays, and `mdeck check` says why; a value outside the range is used, with a warning. `mdeck list themes` shows each theme's settings and what they take, and the design page lets a theme's author set the kind. `mdeck themes build` writes each theme's settings into the gallery's `looks.json`. A parameter without a type takes any text, as before. The built-in themes give every parameter a type and a description with its default and range. A theme that uses `type` needs this version of mdeck.
|
|
@@ -3,7 +3,7 @@ kind = "theme"
|
|
|
3
3
|
id = "glass"
|
|
4
4
|
title = "Glass"
|
|
5
5
|
description = "Liquid glass: soft pools of light behind every slide, content without frames, titles on a frosted pane with a bright rim; capsule labels, glass beads for list markers and a glass lens for chapter numbers; Geist throughout, dark by default."
|
|
6
|
-
version = "0.
|
|
6
|
+
version = "0.3.0"
|
|
7
7
|
author = "Tilman Schieber"
|
|
8
8
|
license = "MIT"
|
|
9
9
|
# The palette this theme uses unless the deck sets one; colours come only from palettes.
|
|
@@ -40,6 +40,8 @@ styles = "styles.css"
|
|
|
40
40
|
# The glass: how round the panes are and how much they blur what is behind them.
|
|
41
41
|
"--glass-radius" = "44px"
|
|
42
42
|
"--glass-blur" = "28px"
|
|
43
|
+
# Whether the light drifts (1) or holds still (0).
|
|
44
|
+
"--motion" = "1"
|
|
43
45
|
|
|
44
46
|
# Type families
|
|
45
47
|
"--font-display" = "\"Geist\", \"Inter\", \"Helvetica Neue\", Arial, sans-serif"
|
|
@@ -64,6 +66,12 @@ min = 0
|
|
|
64
66
|
max = 48
|
|
65
67
|
description = "28px by default, from 0px (clear glass) to about 48px."
|
|
66
68
|
|
|
69
|
+
[params.motion]
|
|
70
|
+
token = "--motion"
|
|
71
|
+
title = "Motion"
|
|
72
|
+
type = "switch"
|
|
73
|
+
description = "1 (the default) lets the pools of light drift while presenting; 0 keeps them still, for recordings or a calm room. Print and reduced motion are always still."
|
|
74
|
+
|
|
67
75
|
# What this theme is for and how to write for it: shown in listings, and read
|
|
68
76
|
# by assistants that write slides as the theme author's advice.
|
|
69
77
|
[guide]
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
to tint. Radial gradients only (no blur filter): they stay vector in a
|
|
54
54
|
PDF and are cheap to draw. */
|
|
55
55
|
background:
|
|
56
|
-
radial-gradient(circle max(var(--a-r) * 2.8, 720px) at calc(var(--a-x) + var(--dx)) calc(var(--a-y) + var(--dy)), var(--wash-a) 0%, color-mix(in oklab, var(--wash-a) 55%, transparent) 40%, transparent 100%),
|
|
57
|
-
radial-gradient(circle max(var(--b-r) * 2.8, 640px) at calc(var(--b-x) - var(--dx)) calc(var(--b-y) - var(--dy)), var(--wash-b) 0%, color-mix(in oklab, var(--wash-b) 55%, transparent) 40%, transparent 100%),
|
|
58
|
-
radial-gradient(circle max(var(--c-r) * 2.6, 520px) at calc(var(--c-x) + var(--dy)) calc(var(--c-y) + var(--dx)), var(--wash-c) 0%, transparent 100%),
|
|
56
|
+
radial-gradient(circle max(var(--a-r) * 2.8, 720px) at calc(var(--a-x) + var(--dx) * var(--motion)) calc(var(--a-y) + var(--dy) * var(--motion)), var(--wash-a) 0%, color-mix(in oklab, var(--wash-a) 55%, transparent) 40%, transparent 100%),
|
|
57
|
+
radial-gradient(circle max(var(--b-r) * 2.8, 640px) at calc(var(--b-x) - var(--dx) * var(--motion)) calc(var(--b-y) - var(--dy) * var(--motion)), var(--wash-b) 0%, color-mix(in oklab, var(--wash-b) 55%, transparent) 40%, transparent 100%),
|
|
58
|
+
radial-gradient(circle max(var(--c-r) * 2.6, 520px) at calc(var(--c-x) + var(--dy) * var(--motion)) calc(var(--c-y) + var(--dx) * var(--motion)), var(--wash-c) 0%, transparent 100%),
|
|
59
59
|
var(--canvas);
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -526,7 +526,7 @@ strong { font-weight: 650; color: var(--ink); }
|
|
|
526
526
|
}
|
|
527
527
|
|
|
528
528
|
/* The light drifts slowly while presenting; never in print or for people
|
|
529
|
-
who prefer less motion. */
|
|
529
|
+
who prefer less motion. motion: 0 holds it still. */
|
|
530
530
|
@media (prefers-reduced-motion: no-preference) {
|
|
531
531
|
.slide:is(.slide--title, .slide--chapter, .slide--focus) { animation: glass-drift 22s ease-in-out infinite alternate; }
|
|
532
532
|
}
|
|
@@ -82,7 +82,10 @@ params:
|
|
|
82
82
|
|
|
83
83
|
Available params depend on the theme; `mdeck list themes` shows them with the
|
|
84
84
|
kind of value each takes: a size in pixels, a number, a switch (`1` or `0`), a
|
|
85
|
-
choice or a
|
|
85
|
+
choice, a font or a text, written as it should appear (`prompt: "~/demo $ "`). `0` or `0px` turns a feature off.
|
|
86
|
+
A font named this way is not downloaded: it has to be installed on the
|
|
87
|
+
computer that shows the slides, or the browser uses the next font in the
|
|
88
|
+
list; PDFs and single files embed only the theme's own fonts. A value that does not fit
|
|
86
89
|
is left out, and `mdeck check` says why. Colours are not params: they come from
|
|
87
90
|
the palette.
|
|
88
91
|
|
|
@@ -201,7 +201,7 @@ Keep headings to a few words; three or four points make a good slide.
|
|
|
201
201
|
`:root` block from them, so the stylesheet only contains rules.
|
|
202
202
|
- `params` name the settings authors may change under `params:` in the deck.
|
|
203
203
|
Each points at a token; its default is the token's value, so nothing is
|
|
204
|
-
written twice. `type` (`size`, `number`, `switch`, `choice` or `
|
|
204
|
+
written twice. `type` (`size`, `number`, `switch`, `choice`, `font` or `text`) says
|
|
205
205
|
what kind of value it takes; `min`, `max` and `step` go with a size (in
|
|
206
206
|
pixels) or a number, `choices` with a choice. The token's value must be of
|
|
207
207
|
that type. The editor shows a slider, a box to tick or a list for it, and
|
package/docs/reference/themes.md
CHANGED
|
@@ -79,7 +79,8 @@ params:
|
|
|
79
79
|
|
|
80
80
|
A parameter's default is its token's value. `type` says what kind of value it
|
|
81
81
|
takes: `size` (pixels, with `min`, `max` and `step`), `number` (with the same
|
|
82
|
-
three), `switch` (1 or 0), `choice` (one of `choices`) or `
|
|
82
|
+
three), `switch` (1 or 0), `choice` (one of `choices`), `font` or `text` (words to show,
|
|
83
|
+
which mdeck quotes; the token's value is a quoted CSS string). A deck value
|
|
83
84
|
that does not fit is left out, and `mdeck check` reports it, like unknown
|
|
84
85
|
parameter names; one outside `min` and `max` is used, with a warning. Without
|
|
85
86
|
a `type`, a parameter takes any text.
|
|
@@ -138,7 +138,7 @@ params:
|
|
|
138
138
|
fontBody: "Georgia, serif"
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
Its default is simply the token's value. Colours are not parameters: they come from the palette.
|
|
141
|
+
Its default is simply the token's value. Colours are not parameters: they come from the palette. A font a deck names this way is not downloaded: it has to be installed on the computer that shows the slides, or the browser uses the next font in the list; PDFs and single files embed only the theme's own fonts.
|
|
142
142
|
|
|
143
143
|
Say what kind of value a parameter takes with `type`, so mdeck can check what authors write and the editor can offer the right control:
|
|
144
144
|
|
|
@@ -159,14 +159,17 @@ description = "12px by default, from about 6px to 20px; 0px leaves headings with
|
|
|
159
159
|
| `switch` | `1` or `0` (also `true`/`false`, `on`/`off`) | a box to tick | |
|
|
160
160
|
| `choice` | one of its `choices` | a list | `choices = ["left", "right"]` |
|
|
161
161
|
| `font` | a CSS font list | a text box | |
|
|
162
|
+
| `text` | words to show, such as a prompt; the author writes them plain and mdeck quotes them | a text box | |
|
|
162
163
|
|
|
163
|
-
The token's value must be a value of that type: a `size` is written `"12px"`, a `switch` `"1"`. A value in the deck that does not fit is left out, and the theme's own value stays; `mdeck check` says why. A value outside `min` and `max` is used, with a warning: the range is what the theme was made for. A parameter without a `type` takes any text, as before.
|
|
164
|
+
The token's value must be a value of that type: a `size` is written `"12px"`, a `switch` `"1"`, a `text` as a quoted CSS string, `"'~/talk $ '"`, used as `content: var(--prompt)`. A value in the deck that does not fit is left out, and the theme's own value stays; `mdeck check` says why. A value outside `min` and `max` is used, with a warning: the range is what the theme was made for. A parameter without a `type` takes any text, as before.
|
|
164
165
|
|
|
165
166
|
A few habits make parameters easy to use:
|
|
166
167
|
|
|
167
168
|
- Turn a feature off with its parameter at zero (`0px`, `0`) rather than a second parameter.
|
|
168
169
|
- Use a switch or a strength by multiplying with it, as in `calc(330px * var(--shapes))`.
|
|
169
170
|
- Let the `description` give the default and a sensible range: the editor shows it under the control, and assistants read it.
|
|
171
|
+
- Offer what people change from one talk to the next, because of the room, the medium or the content, and leave sizes and distances at the values the theme was made with.
|
|
172
|
+
- Use the shared names where they fit, so people learn them once: `motion` (a switch) turns off everything that moves while presenting, for recordings and calm rooms; `texture` (a switch) turns off grain, paper and scanlines, for washed-out projectors and handouts.
|
|
170
173
|
|
|
171
174
|
## Say what it is for
|
|
172
175
|
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@ import { PalettePicker } from './PalettePicker.jsx'
|
|
|
10
10
|
import { LookThumbnail } from './LookThumbnail.jsx'
|
|
11
11
|
import { designUrl, DESIGN_TAB } from '../designLink.js'
|
|
12
12
|
import { normalMode } from '../../runtime/hotspots.js'
|
|
13
|
-
import { paramValue } from '../../extensions/params.js'
|
|
13
|
+
import { paramValue, paramDefault } from '../../extensions/params.js'
|
|
14
14
|
|
|
15
15
|
// What the `show` setting controls: the label for each choice and its values.
|
|
16
16
|
const SHOW = { organization: ['Organization', ['title', 'all', 'none']], author: ['Author and date', ['title', 'all', 'none']], numbers: ['Slide numbers', ['slides', 'all', 'none']], sections: ['Section labels', ['all', 'none']] }
|
|
@@ -112,8 +112,8 @@ function DesignLink({ kind, item }) {
|
|
|
112
112
|
|
|
113
113
|
// The control for one theme parameter, by its type: a slider for a size or a
|
|
114
114
|
// number with a range, a box to tick for a switch, a list for a choice, else
|
|
115
|
-
// text (with a colour picker when the default is a colour). An empty
|
|
116
|
-
// means the theme's own.
|
|
115
|
+
// a text box (with a colour picker when the default is a colour). An empty
|
|
116
|
+
// value means the theme's own; a text shows the theme's without its quotes.
|
|
117
117
|
function ParamInput({ def, value, onChange }) {
|
|
118
118
|
const current = value != null && !paramValue(def, value).problem ? paramValue(def, value).value : def.default
|
|
119
119
|
if (def.type === 'switch') return <>
|
|
@@ -137,6 +137,6 @@ function ParamInput({ def, value, onChange }) {
|
|
|
137
137
|
const color = isColor(def.default)
|
|
138
138
|
return <>
|
|
139
139
|
{color ? <input type="color" value={isColor(value) ? value : def.default} onInput={e => onChange(e.currentTarget.value)} style={{ flex: '0 0 auto' }} /> : null}
|
|
140
|
-
<input type="text" value={value ?? ''} placeholder={def
|
|
140
|
+
<input type="text" value={value ?? ''} placeholder={paramDefault(def)} onInput={e => onChange(e.currentTarget.value)} />
|
|
141
141
|
</>
|
|
142
142
|
}
|
|
@@ -8,7 +8,7 @@ const isColor = value => /^#[0-9a-f]{3,8}$/i.test(String(value ?? '').trim())
|
|
|
8
8
|
const update = (list, index, patch) => list.map((item, i) => i === index ? { ...item, ...patch } : item)
|
|
9
9
|
const without = (list, index) => list.filter((_, i) => i !== index)
|
|
10
10
|
// The kinds of value a theme parameter takes (see src/extensions/params.js).
|
|
11
|
-
const PARAM_KINDS = [['', '
|
|
11
|
+
const PARAM_KINDS = [['', 'CSS value'], ['size', 'size'], ['number', 'number'], ['switch', 'switch'], ['choice', 'choice'], ['font', 'font'], ['text', 'text']]
|
|
12
12
|
|
|
13
13
|
function Identity({ model, onChange }) {
|
|
14
14
|
return <>
|
package/src/extensions/params.js
CHANGED
|
@@ -7,11 +7,13 @@
|
|
|
7
7
|
// switch 1 or 0 (also true and false, on and off, yes and no)
|
|
8
8
|
// choice one of `choices`
|
|
9
9
|
// font a CSS font list
|
|
10
|
+
// text words to show, such as a prompt: the author writes them plain,
|
|
11
|
+
// mdeck quotes them; the theme's own value is a quoted CSS string
|
|
10
12
|
//
|
|
11
13
|
// The manifest check, `mdeck check`, the slides and the editor all use these
|
|
12
14
|
// functions, so they agree on what a value means.
|
|
13
15
|
|
|
14
|
-
export const PARAM_TYPES = ['size', 'number', 'switch', 'choice', 'font']
|
|
16
|
+
export const PARAM_TYPES = ['size', 'number', 'switch', 'choice', 'font', 'text']
|
|
15
17
|
export const PARAM_KEYS = ['token', 'title', 'description', 'type', 'min', 'max', 'step', 'choices']
|
|
16
18
|
const RANGED = ['size', 'number']
|
|
17
19
|
const NUMBER_RE = /^-?(\d+\.?\d*|\.\d+)$/
|
|
@@ -26,6 +28,25 @@ function pixels(value) {
|
|
|
26
28
|
|
|
27
29
|
const unit = param => param.type === 'size' ? 'px' : ''
|
|
28
30
|
|
|
31
|
+
// A CSS string literal ('…' or "…") and the text in it.
|
|
32
|
+
const STRING_RE = /^(['"])((?:\\[^]|(?!\1)[^\\\n])*)\1$/
|
|
33
|
+
|
|
34
|
+
/** Text as a CSS string: quoted, with backslashes, quotes and line breaks escaped. */
|
|
35
|
+
export function cssString(text) {
|
|
36
|
+
return '"' + String(text).replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\r?\n/g, '\\A ') + '"'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The text in a CSS string literal, or null when it is not one. */
|
|
40
|
+
export function unquote(css) {
|
|
41
|
+
const match = STRING_RE.exec(String(css).trim())
|
|
42
|
+
return match ? match[2].replace(/\\A ?/g, '\n').replace(/\\([^])/g, '$1') : null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** A parameter's default as an author would write it: a text without its quotes. */
|
|
46
|
+
export function paramDefault(param) {
|
|
47
|
+
return param.type === 'text' ? unquote(param.default) ?? param.default : param.default
|
|
48
|
+
}
|
|
49
|
+
|
|
29
50
|
/** The range a parameter was made for, as text ("6px to 20px", "at least 0"), or ''. */
|
|
30
51
|
export function paramRange(param) {
|
|
31
52
|
const { min, max } = param
|
|
@@ -54,6 +75,8 @@ export function paramValue(param, raw) {
|
|
|
54
75
|
const type = param.type
|
|
55
76
|
if (raw == null || (typeof raw === 'object')) return { problem: 'needs a single value' }
|
|
56
77
|
if (!type) return { value: String(raw) }
|
|
78
|
+
// Text keeps its spaces (a prompt ends in one) and may be empty.
|
|
79
|
+
if (type === 'text') return { value: cssString(raw) }
|
|
57
80
|
const text = String(raw).trim()
|
|
58
81
|
if (!text) return { problem: 'is empty' }
|
|
59
82
|
if (type === 'font') return { value: text }
|
|
@@ -98,6 +121,10 @@ export function paramSettingProblems(param, fallback) {
|
|
|
98
121
|
else if (new Set(choices).size !== choices.length) problems.push(['choices', 'lists a value twice'])
|
|
99
122
|
} else if (param.choices != null) problems.push(['choices', `is for a choice, not a ${type}`])
|
|
100
123
|
if (problems.length || fallback == null) return problems
|
|
124
|
+
if (type === 'text') {
|
|
125
|
+
if (unquote(fallback) == null) problems.push(['type', `a text's token is a quoted CSS string, such as "'~/talk $ '" (got ${JSON.stringify(fallback)})`])
|
|
126
|
+
return problems
|
|
127
|
+
}
|
|
101
128
|
// The default is the token's value: it has to be a value of this type too.
|
|
102
129
|
const result = paramValue(param, fallback)
|
|
103
130
|
if (result.problem) problems.push(['type', `the token's value ${JSON.stringify(fallback)} ${result.problem}`])
|