@dustfeather/deckrun 2.0.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/LICENSE +21 -0
- package/README.md +1073 -0
- package/THIRD-PARTY-NOTICES.md +38 -0
- package/dist/editor-content.js +485 -0
- package/dist/editor.js +3916 -0
- package/dist/fragments.js +71 -0
- package/dist/generate.js +3488 -0
- package/dist/highlights.js +833 -0
- package/dist/index.js +1020 -0
- package/dist/lint.js +330 -0
- package/dist/parser.js +221 -0
- package/dist/pdf.js +200 -0
- package/dist/presentation-options.js +289 -0
- package/dist/preview.js +400 -0
- package/dist/rich-content.js +195 -0
- package/dist/safe-fetch.js +173 -0
- package/dist/sanitize.js +102 -0
- package/dist/themes.js +1041 -0
- package/dist/titles.js +30 -0
- package/package.json +64 -0
package/dist/pdf.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
|
+
import { access, mkdtemp, readFile, rm, stat } from "fs/promises";
|
|
3
|
+
import { constants } from "fs";
|
|
4
|
+
import { tmpdir } from "os";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
/**
|
|
7
|
+
* PDF export without the print dialog.
|
|
8
|
+
*
|
|
9
|
+
* The deck already prints correctly, but only if the person exporting picks
|
|
10
|
+
* landscape and turns on background graphics. Driving a headless browser
|
|
11
|
+
* ourselves removes that step: same renderer, same stylesheet, no choices.
|
|
12
|
+
*
|
|
13
|
+
* Nothing is installed for this. It uses a Chromium-family browser that is
|
|
14
|
+
* already on the machine, and the caller falls back to the print dialog when
|
|
15
|
+
* there is not one.
|
|
16
|
+
*/
|
|
17
|
+
const CANDIDATES = {
|
|
18
|
+
darwin: [
|
|
19
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
20
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
|
21
|
+
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
|
|
22
|
+
"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
|
|
23
|
+
"/Applications/Arc.app/Contents/MacOS/Arc",
|
|
24
|
+
"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
|
|
25
|
+
],
|
|
26
|
+
linux: [
|
|
27
|
+
"/usr/bin/google-chrome",
|
|
28
|
+
"/usr/bin/google-chrome-stable",
|
|
29
|
+
"/usr/bin/chromium",
|
|
30
|
+
"/usr/bin/chromium-browser",
|
|
31
|
+
"/usr/bin/microsoft-edge",
|
|
32
|
+
"/usr/bin/brave-browser",
|
|
33
|
+
"/snap/bin/chromium",
|
|
34
|
+
"/opt/google/chrome/chrome",
|
|
35
|
+
],
|
|
36
|
+
win32: [
|
|
37
|
+
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
|
|
38
|
+
"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
|
|
39
|
+
"C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe",
|
|
40
|
+
"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
|
|
41
|
+
"C:\\Program Files\\Chromium\\Application\\chrome.exe",
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
/** Env overrides, checked before the well-known locations. */
|
|
45
|
+
const ENV_KEYS = [
|
|
46
|
+
"DECKRUN_BROWSER",
|
|
47
|
+
"CHROME_PATH",
|
|
48
|
+
"PUPPETEER_EXECUTABLE_PATH",
|
|
49
|
+
];
|
|
50
|
+
let cached;
|
|
51
|
+
async function isExecutable(path) {
|
|
52
|
+
try {
|
|
53
|
+
await access(path, constants.X_OK);
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Path to a usable browser, or null when the machine has none we recognise. */
|
|
61
|
+
export async function findBrowser() {
|
|
62
|
+
if (cached !== undefined)
|
|
63
|
+
return cached;
|
|
64
|
+
for (const key of ENV_KEYS) {
|
|
65
|
+
const value = process.env[key];
|
|
66
|
+
if (value && (await isExecutable(value))) {
|
|
67
|
+
cached = value;
|
|
68
|
+
return cached;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
for (const path of CANDIDATES[process.platform] ?? []) {
|
|
72
|
+
if (await isExecutable(path)) {
|
|
73
|
+
cached = path;
|
|
74
|
+
return cached;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
cached = null;
|
|
78
|
+
return cached;
|
|
79
|
+
}
|
|
80
|
+
export class PdfError extends Error {
|
|
81
|
+
}
|
|
82
|
+
const RENDER_TIMEOUT_MS = 45_000;
|
|
83
|
+
const POLL_MS = 150;
|
|
84
|
+
function sleep(ms) {
|
|
85
|
+
return new Promise((done) => setTimeout(done, ms));
|
|
86
|
+
}
|
|
87
|
+
function killTree(child) {
|
|
88
|
+
try {
|
|
89
|
+
// Chrome leaves helper processes behind, so kill the whole group where the
|
|
90
|
+
// platform has them.
|
|
91
|
+
if (process.platform !== "win32" && child.pid)
|
|
92
|
+
process.kill(-child.pid, "SIGKILL");
|
|
93
|
+
else
|
|
94
|
+
child.kill("SIGKILL");
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Already gone.
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Waits for the PDF itself rather than for the browser to exit.
|
|
102
|
+
*
|
|
103
|
+
* Chrome writes the file in about a second and then lingers, running update
|
|
104
|
+
* checks and other background work, so waiting on process exit would stall for
|
|
105
|
+
* as long as the timeout allows. A size that stops changing means the write is
|
|
106
|
+
* finished.
|
|
107
|
+
*/
|
|
108
|
+
async function waitForPdf(out, hasExited, spawnError) {
|
|
109
|
+
const deadline = Date.now() + RENDER_TIMEOUT_MS;
|
|
110
|
+
let lastSize = -1;
|
|
111
|
+
while (Date.now() < deadline) {
|
|
112
|
+
const failure = spawnError();
|
|
113
|
+
if (failure)
|
|
114
|
+
throw failure;
|
|
115
|
+
let size = -1;
|
|
116
|
+
try {
|
|
117
|
+
size = (await stat(out)).size;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// Not written yet.
|
|
121
|
+
}
|
|
122
|
+
if (size > 0 && size === lastSize)
|
|
123
|
+
return readFile(out);
|
|
124
|
+
lastSize = size;
|
|
125
|
+
if (hasExited() && size <= 0) {
|
|
126
|
+
// One last look, in case the write landed as the process was leaving.
|
|
127
|
+
try {
|
|
128
|
+
if ((await stat(out)).size > 0)
|
|
129
|
+
return readFile(out);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Nothing there.
|
|
133
|
+
}
|
|
134
|
+
throw new PdfError("the browser exited without producing a PDF");
|
|
135
|
+
}
|
|
136
|
+
await sleep(POLL_MS);
|
|
137
|
+
}
|
|
138
|
+
throw new PdfError("the browser took too long to render the deck");
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Renders a served deck to PDF bytes.
|
|
142
|
+
*
|
|
143
|
+
* `--virtual-time-budget` matters: without it the browser prints before the
|
|
144
|
+
* webfont and the syntax highlighter have arrived, and the PDF comes out in a
|
|
145
|
+
* fallback font with plain code blocks.
|
|
146
|
+
*/
|
|
147
|
+
export async function renderPdf(url, browser) {
|
|
148
|
+
const dir = await mkdtemp(join(tmpdir(), "deckrun-pdf-"));
|
|
149
|
+
const out = join(dir, "deck.pdf");
|
|
150
|
+
const args = [
|
|
151
|
+
"--headless",
|
|
152
|
+
"--disable-gpu",
|
|
153
|
+
"--hide-scrollbars",
|
|
154
|
+
"--no-first-run",
|
|
155
|
+
"--no-default-browser-check",
|
|
156
|
+
"--disable-extensions",
|
|
157
|
+
"--disable-sync",
|
|
158
|
+
"--disable-default-apps",
|
|
159
|
+
"--disable-component-update",
|
|
160
|
+
"--no-service-autorun",
|
|
161
|
+
"--mute-audio",
|
|
162
|
+
// Never touch the browser profile the person is actually using.
|
|
163
|
+
`--user-data-dir=${join(dir, "profile")}`,
|
|
164
|
+
// Mermaid performs an asynchronous layout pass after its local script has
|
|
165
|
+
// loaded. Give that pass room to settle and flush every compositor stage
|
|
166
|
+
// before Chrome snapshots the pages.
|
|
167
|
+
"--virtual-time-budget=10000",
|
|
168
|
+
"--run-all-compositor-stages-before-draw",
|
|
169
|
+
// Header/footer flag names differ across versions; unknown switches are ignored.
|
|
170
|
+
"--no-pdf-header-footer",
|
|
171
|
+
"--print-to-pdf-no-header",
|
|
172
|
+
`--print-to-pdf=${out}`,
|
|
173
|
+
url,
|
|
174
|
+
];
|
|
175
|
+
let exited = false;
|
|
176
|
+
let failure = null;
|
|
177
|
+
const child = spawn(browser, args, {
|
|
178
|
+
stdio: "ignore",
|
|
179
|
+
detached: process.platform !== "win32",
|
|
180
|
+
});
|
|
181
|
+
child.on("exit", () => { exited = true; });
|
|
182
|
+
child.on("error", (err) => {
|
|
183
|
+
failure = new PdfError(`could not run ${browser}: ${err.message}`);
|
|
184
|
+
exited = true;
|
|
185
|
+
});
|
|
186
|
+
try {
|
|
187
|
+
return await waitForPdf(out, () => exited, () => failure);
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
killTree(child);
|
|
191
|
+
await rm(dir, { recursive: true, force: true }).catch(() => { });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/** One render at a time, so a stuck or repeated request cannot spawn a fleet. */
|
|
195
|
+
let queue = Promise.resolve();
|
|
196
|
+
export function renderPdfSerial(url, browser) {
|
|
197
|
+
const run = queue.then(() => renderPdf(url, browser), () => renderPdf(url, browser));
|
|
198
|
+
queue = run.catch(() => { });
|
|
199
|
+
return run;
|
|
200
|
+
}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
export const DEFAULT_TEMPLATE = "classic";
|
|
2
|
+
export const DEFAULT_TRANSITION = "slide";
|
|
3
|
+
export const TEMPLATE_IDS = [
|
|
4
|
+
"classic",
|
|
5
|
+
"minimal",
|
|
6
|
+
"editorial",
|
|
7
|
+
"spotlight",
|
|
8
|
+
];
|
|
9
|
+
export const TRANSITION_IDS = [
|
|
10
|
+
"slide",
|
|
11
|
+
"fade",
|
|
12
|
+
"zoom",
|
|
13
|
+
"lift",
|
|
14
|
+
"none",
|
|
15
|
+
];
|
|
16
|
+
export const TEMPLATE_SPECS = {
|
|
17
|
+
classic: {
|
|
18
|
+
label: "Classic",
|
|
19
|
+
blurb: "The original balanced deckrun layout",
|
|
20
|
+
},
|
|
21
|
+
minimal: {
|
|
22
|
+
label: "Minimal",
|
|
23
|
+
blurb: "Quiet surfaces, wider margins, fewer decorative treatments",
|
|
24
|
+
},
|
|
25
|
+
editorial: {
|
|
26
|
+
label: "Editorial",
|
|
27
|
+
blurb: "Strong rules and magazine-like reading rhythm",
|
|
28
|
+
},
|
|
29
|
+
spotlight: {
|
|
30
|
+
label: "Spotlight",
|
|
31
|
+
blurb: "Centered, high-impact composition for concise keynote slides",
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
export const TRANSITION_SPECS = {
|
|
35
|
+
slide: {
|
|
36
|
+
label: "Slide",
|
|
37
|
+
blurb: "Horizontal sliding transition between slides",
|
|
38
|
+
},
|
|
39
|
+
fade: {
|
|
40
|
+
label: "Fade",
|
|
41
|
+
blurb: "Cross-fade between slides",
|
|
42
|
+
},
|
|
43
|
+
zoom: {
|
|
44
|
+
label: "Zoom",
|
|
45
|
+
blurb: "Scale up and down between slides",
|
|
46
|
+
},
|
|
47
|
+
lift: {
|
|
48
|
+
label: "Lift",
|
|
49
|
+
blurb: "Vertical rising transition between slides",
|
|
50
|
+
},
|
|
51
|
+
none: {
|
|
52
|
+
label: "None",
|
|
53
|
+
blurb: "Instant cut between slides",
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
export function findTemplate(input) {
|
|
57
|
+
if (!input)
|
|
58
|
+
return null;
|
|
59
|
+
const key = String(input).trim().toLowerCase();
|
|
60
|
+
for (const id of TEMPLATE_IDS) {
|
|
61
|
+
if (id === key)
|
|
62
|
+
return id;
|
|
63
|
+
if (TEMPLATE_SPECS[id].label.toLowerCase() === key)
|
|
64
|
+
return id;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
export function resolveTemplateName(input) {
|
|
69
|
+
return findTemplate(input) ?? DEFAULT_TEMPLATE;
|
|
70
|
+
}
|
|
71
|
+
export function templateSummaries() {
|
|
72
|
+
return TEMPLATE_IDS.map((id) => ({
|
|
73
|
+
id,
|
|
74
|
+
label: TEMPLATE_SPECS[id].label,
|
|
75
|
+
blurb: TEMPLATE_SPECS[id].blurb,
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
export function templateListing() {
|
|
79
|
+
const pad = Math.max(...TEMPLATE_IDS.map((id) => id.length));
|
|
80
|
+
return TEMPLATE_IDS.map((id) => {
|
|
81
|
+
const s = TEMPLATE_SPECS[id];
|
|
82
|
+
return `${id.padEnd(pad)} ${s.label.padEnd(10)} ${s.blurb}`;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
export function findTransition(input) {
|
|
86
|
+
if (!input)
|
|
87
|
+
return null;
|
|
88
|
+
const key = String(input).trim().toLowerCase();
|
|
89
|
+
for (const id of TRANSITION_IDS) {
|
|
90
|
+
if (id === key)
|
|
91
|
+
return id;
|
|
92
|
+
if (TRANSITION_SPECS[id].label.toLowerCase() === key)
|
|
93
|
+
return id;
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
export function resolveTransitionName(input) {
|
|
98
|
+
return findTransition(input) ?? DEFAULT_TRANSITION;
|
|
99
|
+
}
|
|
100
|
+
export function transitionSummaries() {
|
|
101
|
+
return TRANSITION_IDS.map((id) => ({
|
|
102
|
+
id,
|
|
103
|
+
label: TRANSITION_SPECS[id].label,
|
|
104
|
+
blurb: TRANSITION_SPECS[id].blurb,
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
export function transitionListing() {
|
|
108
|
+
const pad = Math.max(...TRANSITION_IDS.map((id) => id.length));
|
|
109
|
+
return TRANSITION_IDS.map((id) => {
|
|
110
|
+
const s = TRANSITION_SPECS[id];
|
|
111
|
+
return `${id.padEnd(pad)} ${s.label.padEnd(8)} ${s.blurb}`;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
export const TEMPLATE_CSS = `/* ── Templates ──────────────────────────────────────────────────────────── */
|
|
115
|
+
|
|
116
|
+
/* Minimal */
|
|
117
|
+
:root[data-template="minimal"] {
|
|
118
|
+
--slide-pad-x: 10vw;
|
|
119
|
+
--slide-pad-y: 8vh;
|
|
120
|
+
}
|
|
121
|
+
:root[data-template="minimal"] #backdrop {
|
|
122
|
+
opacity: 0.15;
|
|
123
|
+
}
|
|
124
|
+
:root[data-template="minimal"] .slide__content h1 {
|
|
125
|
+
letter-spacing: -0.02em;
|
|
126
|
+
}
|
|
127
|
+
:root[data-template="minimal"] blockquote {
|
|
128
|
+
border-left-width: 2px;
|
|
129
|
+
background: transparent;
|
|
130
|
+
}
|
|
131
|
+
:root[data-template="minimal"] pre {
|
|
132
|
+
border: 1px solid var(--surface0);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* Editorial */
|
|
136
|
+
:root[data-template="editorial"] {
|
|
137
|
+
--slide-pad-x: 7vw;
|
|
138
|
+
}
|
|
139
|
+
:root[data-template="editorial"] .slide__content h1 {
|
|
140
|
+
border-bottom: 3px solid var(--accent);
|
|
141
|
+
padding-bottom: 0.3em;
|
|
142
|
+
margin-bottom: 0.6em;
|
|
143
|
+
}
|
|
144
|
+
:root[data-template="editorial"] .slide__content h2 {
|
|
145
|
+
border-bottom: 1px solid var(--surface1);
|
|
146
|
+
padding-bottom: 0.2em;
|
|
147
|
+
}
|
|
148
|
+
:root[data-template="editorial"] blockquote {
|
|
149
|
+
border-left: 4px solid var(--accent);
|
|
150
|
+
font-style: italic;
|
|
151
|
+
background: var(--surface0);
|
|
152
|
+
}
|
|
153
|
+
:root[data-template="editorial"] table th {
|
|
154
|
+
border-bottom: 2px solid var(--accent);
|
|
155
|
+
}
|
|
156
|
+
:root[data-template="editorial"] hr {
|
|
157
|
+
border: none;
|
|
158
|
+
border-top: 2px solid var(--surface2);
|
|
159
|
+
margin: 2em 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* Spotlight */
|
|
163
|
+
:root[data-template="spotlight"] .slide {
|
|
164
|
+
justify-content: center;
|
|
165
|
+
align-items: center;
|
|
166
|
+
text-align: center;
|
|
167
|
+
}
|
|
168
|
+
:root[data-template="spotlight"] .slide__content {
|
|
169
|
+
display: flex;
|
|
170
|
+
flex-direction: column;
|
|
171
|
+
align-items: center;
|
|
172
|
+
justify-content: center;
|
|
173
|
+
text-align: center;
|
|
174
|
+
}
|
|
175
|
+
:root[data-template="spotlight"] .slide__content h1 {
|
|
176
|
+
/* A shade larger than a normal heading: on a template built around one
|
|
177
|
+
statement a slide, the statement carries it. */
|
|
178
|
+
font-size: calc(clamp(2.1rem, 4.6vw, 3.6rem) * 1.15);
|
|
179
|
+
text-align: center;
|
|
180
|
+
}
|
|
181
|
+
:root[data-template="spotlight"] .slide__content p {
|
|
182
|
+
max-width: 80%;
|
|
183
|
+
margin-left: auto;
|
|
184
|
+
margin-right: auto;
|
|
185
|
+
}
|
|
186
|
+
:root[data-template="spotlight"] .slide__content ul,
|
|
187
|
+
:root[data-template="spotlight"] .slide__content ol {
|
|
188
|
+
text-align: left;
|
|
189
|
+
display: inline-block;
|
|
190
|
+
margin-left: auto;
|
|
191
|
+
margin-right: auto;
|
|
192
|
+
}
|
|
193
|
+
:root[data-template="spotlight"] .slide__content blockquote {
|
|
194
|
+
text-align: center;
|
|
195
|
+
border-left: none;
|
|
196
|
+
border-top: 2px solid var(--accent);
|
|
197
|
+
border-bottom: 2px solid var(--accent);
|
|
198
|
+
padding: 1em 2em;
|
|
199
|
+
background: transparent;
|
|
200
|
+
}
|
|
201
|
+
:root[data-template="spotlight"] .slide__content pre {
|
|
202
|
+
text-align: left;
|
|
203
|
+
}
|
|
204
|
+
`;
|
|
205
|
+
export const TRANSITION_CSS = `/* ── Transitions ────────────────────────────────────────────────────────── */
|
|
206
|
+
|
|
207
|
+
/* Fade */
|
|
208
|
+
:root[data-transition="fade"] .slide {
|
|
209
|
+
transition: opacity 0.3s ease;
|
|
210
|
+
transform: none !important;
|
|
211
|
+
}
|
|
212
|
+
:root[data-transition="fade"] .slide.exit-left,
|
|
213
|
+
:root[data-transition="fade"] .slide.exit-right,
|
|
214
|
+
:root[data-transition="fade"] .slide.enter-from-left,
|
|
215
|
+
:root[data-transition="fade"] .slide.enter-from-right {
|
|
216
|
+
transform: none !important;
|
|
217
|
+
opacity: 0;
|
|
218
|
+
}
|
|
219
|
+
:root[data-transition="fade"] .slide.is-active {
|
|
220
|
+
opacity: 1;
|
|
221
|
+
transform: none !important;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* Zoom */
|
|
225
|
+
:root[data-transition="zoom"] .slide {
|
|
226
|
+
transform: scale(0.92);
|
|
227
|
+
transition: opacity 0.35s ease, transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
|
228
|
+
}
|
|
229
|
+
:root[data-transition="zoom"] .slide.is-active {
|
|
230
|
+
transform: scale(1);
|
|
231
|
+
opacity: 1;
|
|
232
|
+
}
|
|
233
|
+
:root[data-transition="zoom"] .slide.exit-left,
|
|
234
|
+
:root[data-transition="zoom"] .slide.exit-right {
|
|
235
|
+
transform: scale(1.08);
|
|
236
|
+
opacity: 0;
|
|
237
|
+
}
|
|
238
|
+
:root[data-transition="zoom"] .slide.enter-from-left,
|
|
239
|
+
:root[data-transition="zoom"] .slide.enter-from-right {
|
|
240
|
+
transform: scale(0.92);
|
|
241
|
+
opacity: 0;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* Lift */
|
|
245
|
+
:root[data-transition="lift"] .slide {
|
|
246
|
+
transform: translateY(48px);
|
|
247
|
+
transition: opacity 0.35s ease, transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
|
248
|
+
}
|
|
249
|
+
:root[data-transition="lift"] .slide.is-active {
|
|
250
|
+
transform: translateY(0);
|
|
251
|
+
opacity: 1;
|
|
252
|
+
}
|
|
253
|
+
:root[data-transition="lift"] .slide.exit-left,
|
|
254
|
+
:root[data-transition="lift"] .slide.exit-right {
|
|
255
|
+
transform: translateY(-48px);
|
|
256
|
+
opacity: 0;
|
|
257
|
+
}
|
|
258
|
+
:root[data-transition="lift"] .slide.enter-from-left,
|
|
259
|
+
:root[data-transition="lift"] .slide.enter-from-right {
|
|
260
|
+
transform: translateY(48px);
|
|
261
|
+
opacity: 0;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/* None */
|
|
265
|
+
:root[data-transition="none"] .slide,
|
|
266
|
+
:root[data-transition="none"] .slide.is-active,
|
|
267
|
+
:root[data-transition="none"] .slide.exit-left,
|
|
268
|
+
:root[data-transition="none"] .slide.exit-right,
|
|
269
|
+
:root[data-transition="none"] .slide.enter-from-left,
|
|
270
|
+
:root[data-transition="none"] .slide.enter-from-right {
|
|
271
|
+
transition: none !important;
|
|
272
|
+
transform: none !important;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
@media (prefers-reduced-motion: reduce) {
|
|
276
|
+
:root[data-transition] .slide {
|
|
277
|
+
transition: opacity 0.2s linear !important;
|
|
278
|
+
transform: none !important;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
@media print {
|
|
283
|
+
:root[data-transition] .slide {
|
|
284
|
+
transition: none !important;
|
|
285
|
+
transform: none !important;
|
|
286
|
+
opacity: 1 !important;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
`;
|