slides-grab 1.1.2 → 1.1.3
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/README.md +4 -0
- package/bin/ppt-agent.js +8 -0
- package/convert.cjs +35 -7
- package/package.json +1 -1
- package/scripts/html2pdf.js +62 -5
- package/src/export-resolution.cjs +58 -0
package/README.md
CHANGED
|
@@ -69,8 +69,10 @@ slides-grab edit # Launch visual slide editor
|
|
|
69
69
|
slides-grab build-viewer # Build single-file viewer.html
|
|
70
70
|
slides-grab validate # Validate slide HTML (Playwright-based)
|
|
71
71
|
slides-grab convert # Export to experimental / unstable PPTX
|
|
72
|
+
slides-grab convert --resolution 2160p # Higher-resolution raster PPTX export
|
|
72
73
|
slides-grab figma # Export an experimental / unstable Figma Slides importable PPTX
|
|
73
74
|
slides-grab pdf # Export PDF in capture mode (default)
|
|
75
|
+
slides-grab pdf --resolution 2160p # Higher-resolution image-backed PDF export
|
|
74
76
|
slides-grab pdf --mode print # Export searchable/selectable text PDF
|
|
75
77
|
slides-grab list-templates # Show available slide templates
|
|
76
78
|
slides-grab list-themes # Show available color themes
|
|
@@ -89,6 +91,8 @@ Run `slides-grab validate --slides-dir <path>` before export to catch missing lo
|
|
|
89
91
|
|
|
90
92
|
`slides-grab pdf` now defaults to `--mode capture`, which rasterizes each rendered slide into the PDF for better visual fidelity. Use `--mode print` when searchable/selectable browser text matters more than pixel-perfect parity.
|
|
91
93
|
|
|
94
|
+
`slides-grab pdf` and `slides-grab convert` now default to `2160p` / `4k` raster output for sharper exports. You can still override with `--resolution <preset>` using `720p`, `1080p`, `1440p`, `2160p`, or `4k` when you want smaller or faster artifacts.
|
|
95
|
+
|
|
92
96
|
### Multi-Deck Workflow
|
|
93
97
|
|
|
94
98
|
Prerequisite: create or generate a deck in `decks/my-deck/` first.
|
package/bin/ppt-agent.js
CHANGED
|
@@ -106,11 +106,15 @@ program
|
|
|
106
106
|
.description('Convert slide HTML files to experimental / unstable PPTX')
|
|
107
107
|
.option('--slides-dir <path>', 'Slide directory', 'slides')
|
|
108
108
|
.option('--output <path>', 'Output PPTX file')
|
|
109
|
+
.option('--resolution <preset>', 'Raster size preset: 720p, 1080p, 1440p, 2160p, or 4k (default: 2160p)')
|
|
109
110
|
.action(async (options = {}) => {
|
|
110
111
|
const args = ['--slides-dir', options.slidesDir];
|
|
111
112
|
if (options.output) {
|
|
112
113
|
args.push('--output', String(options.output));
|
|
113
114
|
}
|
|
115
|
+
if (options.resolution) {
|
|
116
|
+
args.push('--resolution', String(options.resolution));
|
|
117
|
+
}
|
|
114
118
|
await runCommand('convert.cjs', args);
|
|
115
119
|
});
|
|
116
120
|
|
|
@@ -120,6 +124,7 @@ program
|
|
|
120
124
|
.option('--slides-dir <path>', 'Slide directory', 'slides')
|
|
121
125
|
.option('--output <path>', 'Output PDF file')
|
|
122
126
|
.option('--mode <mode>', 'PDF export mode: capture for visual fidelity, print for searchable text', 'capture')
|
|
127
|
+
.option('--resolution <preset>', 'Capture raster size preset: 720p, 1080p, 1440p, 2160p, or 4k (default: 2160p in capture mode)')
|
|
123
128
|
.action(async (options = {}) => {
|
|
124
129
|
const args = ['--slides-dir', options.slidesDir];
|
|
125
130
|
if (options.output) {
|
|
@@ -128,6 +133,9 @@ program
|
|
|
128
133
|
if (options.mode) {
|
|
129
134
|
args.push('--mode', String(options.mode));
|
|
130
135
|
}
|
|
136
|
+
if (options.resolution) {
|
|
137
|
+
args.push('--resolution', String(options.resolution));
|
|
138
|
+
}
|
|
131
139
|
await runCommand('scripts/html2pdf.js', args);
|
|
132
140
|
});
|
|
133
141
|
|
package/convert.cjs
CHANGED
|
@@ -3,10 +3,16 @@ const { chromium } = require('playwright');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const sharp = require('sharp');
|
|
6
|
+
const {
|
|
7
|
+
getResolutionChoices,
|
|
8
|
+
getResolutionSize,
|
|
9
|
+
normalizeResolutionPreset,
|
|
10
|
+
} = require('./src/export-resolution.cjs');
|
|
6
11
|
|
|
7
12
|
// Inline a simplified version that uses Playwright Chromium (not Chrome)
|
|
8
13
|
const DEFAULT_SLIDES_DIR = 'slides';
|
|
9
14
|
const DEFAULT_OUTPUT = 'output.pptx';
|
|
15
|
+
const DEFAULT_RESOLUTION = '2160p';
|
|
10
16
|
const DEFAULT_CAPTURE_VIEWPORT = { width: 960, height: 540 };
|
|
11
17
|
const DEFAULT_CAPTURE_DEVICE_SCALE_FACTOR = 2;
|
|
12
18
|
const TARGET_RASTER_DPI = 150;
|
|
@@ -19,17 +25,25 @@ function normalizeDimension(value, fallback) {
|
|
|
19
25
|
return Math.max(1, Math.round(value));
|
|
20
26
|
}
|
|
21
27
|
|
|
22
|
-
function buildPageOptions() {
|
|
28
|
+
function buildPageOptions(resolution = '') {
|
|
29
|
+
const targetResolution = getResolutionSize(resolution);
|
|
23
30
|
return {
|
|
24
31
|
viewport: {
|
|
25
32
|
width: DEFAULT_CAPTURE_VIEWPORT.width,
|
|
26
33
|
height: DEFAULT_CAPTURE_VIEWPORT.height,
|
|
27
34
|
},
|
|
28
|
-
deviceScaleFactor:
|
|
35
|
+
deviceScaleFactor: targetResolution
|
|
36
|
+
? targetResolution.height / DEFAULT_CAPTURE_VIEWPORT.height
|
|
37
|
+
: DEFAULT_CAPTURE_DEVICE_SCALE_FACTOR,
|
|
29
38
|
};
|
|
30
39
|
}
|
|
31
40
|
|
|
32
|
-
function getTargetRasterSize() {
|
|
41
|
+
function getTargetRasterSize(resolution = '') {
|
|
42
|
+
const targetResolution = getResolutionSize(resolution);
|
|
43
|
+
if (targetResolution) {
|
|
44
|
+
return targetResolution;
|
|
45
|
+
}
|
|
46
|
+
|
|
33
47
|
return {
|
|
34
48
|
width: Math.round(TARGET_SLIDE_SIZE_IN.width * TARGET_RASTER_DPI),
|
|
35
49
|
height: Math.round(TARGET_SLIDE_SIZE_IN.height * TARGET_RASTER_DPI),
|
|
@@ -44,6 +58,7 @@ function printUsage() {
|
|
|
44
58
|
'Options:',
|
|
45
59
|
` --slides-dir <path> Slide directory (default: ${DEFAULT_SLIDES_DIR})`,
|
|
46
60
|
` --output <path> Output pptx path (default: ${DEFAULT_OUTPUT})`,
|
|
61
|
+
` --resolution <preset> Raster size preset: ${getResolutionChoices().join('|')}|4k (default: ${DEFAULT_RESOLUTION})`,
|
|
47
62
|
' -h, --help Show this help message',
|
|
48
63
|
].join('\n'),
|
|
49
64
|
);
|
|
@@ -62,6 +77,7 @@ function parseArgs(args) {
|
|
|
62
77
|
const options = {
|
|
63
78
|
slidesDir: DEFAULT_SLIDES_DIR,
|
|
64
79
|
output: DEFAULT_OUTPUT,
|
|
80
|
+
resolution: DEFAULT_RESOLUTION,
|
|
65
81
|
help: false,
|
|
66
82
|
};
|
|
67
83
|
|
|
@@ -94,6 +110,17 @@ function parseArgs(args) {
|
|
|
94
110
|
continue;
|
|
95
111
|
}
|
|
96
112
|
|
|
113
|
+
if (arg === '--resolution') {
|
|
114
|
+
options.resolution = normalizeResolutionPreset(readOptionValue(args, i, '--resolution'));
|
|
115
|
+
i += 1;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (arg.startsWith('--resolution=')) {
|
|
120
|
+
options.resolution = normalizeResolutionPreset(arg.slice('--resolution='.length));
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
|
|
97
124
|
throw new Error(`Unknown option: ${arg}`);
|
|
98
125
|
}
|
|
99
126
|
|
|
@@ -107,13 +134,14 @@ function parseArgs(args) {
|
|
|
107
134
|
|
|
108
135
|
options.slidesDir = options.slidesDir.trim();
|
|
109
136
|
options.output = options.output.trim();
|
|
137
|
+
options.resolution = normalizeResolutionPreset(options.resolution);
|
|
110
138
|
return options;
|
|
111
139
|
}
|
|
112
140
|
|
|
113
|
-
async function convertSlide(htmlFile, pres, browser) {
|
|
141
|
+
async function convertSlide(htmlFile, pres, browser, options = {}) {
|
|
114
142
|
const filePath = path.isAbsolute(htmlFile) ? htmlFile : path.join(process.cwd(), htmlFile);
|
|
115
143
|
|
|
116
|
-
const page = await browser.newPage(buildPageOptions());
|
|
144
|
+
const page = await browser.newPage(buildPageOptions(options.resolution));
|
|
117
145
|
await page.goto(`file://${filePath}`);
|
|
118
146
|
|
|
119
147
|
const bodyDimensions = await page.evaluate(() => {
|
|
@@ -135,7 +163,7 @@ async function convertSlide(htmlFile, pres, browser) {
|
|
|
135
163
|
await page.close();
|
|
136
164
|
|
|
137
165
|
// Resize to exact slide dimensions (13.33" x 7.5" at 150 DPI)
|
|
138
|
-
const targetSize = getTargetRasterSize();
|
|
166
|
+
const targetSize = getTargetRasterSize(options.resolution);
|
|
139
167
|
|
|
140
168
|
const resized = await sharp(screenshot)
|
|
141
169
|
.resize(targetSize.width, targetSize.height, { fit: 'fill' })
|
|
@@ -184,7 +212,7 @@ async function main() {
|
|
|
184
212
|
const filePath = path.join(slidesDir, file);
|
|
185
213
|
console.log(` Processing: ${file}`);
|
|
186
214
|
try {
|
|
187
|
-
const tmpPath = await convertSlide(filePath, pres, browser);
|
|
215
|
+
const tmpPath = await convertSlide(filePath, pres, browser, { resolution: options.resolution });
|
|
188
216
|
tmpFiles.push(tmpPath);
|
|
189
217
|
console.log(` ✓ ${file} done`);
|
|
190
218
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slides-grab",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "Agent-first presentation framework — plan, design, and visually edit HTML slides with Claude Code or Codex, then export to PDF or experimental/unstable PPTX/Figma formats",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "vkehfdl1",
|
package/scripts/html2pdf.js
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { readdir, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
4
5
|
import { basename, join, resolve } from 'node:path';
|
|
5
6
|
import { pathToFileURL } from 'node:url';
|
|
6
7
|
import { chromium } from 'playwright';
|
|
7
8
|
import { PDFDocument } from 'pdf-lib';
|
|
9
|
+
import sharp from 'sharp';
|
|
8
10
|
|
|
9
11
|
import { ensureSlidesPassValidation } from './validate-slides.js';
|
|
10
12
|
|
|
13
|
+
const require = createRequire(import.meta.url);
|
|
14
|
+
const {
|
|
15
|
+
getResolutionChoices,
|
|
16
|
+
getResolutionSize,
|
|
17
|
+
normalizeResolutionPreset,
|
|
18
|
+
} = require('../src/export-resolution.cjs');
|
|
19
|
+
|
|
11
20
|
const DEFAULT_OUTPUT = 'slides.pdf';
|
|
12
21
|
const DEFAULT_SLIDES_DIR = 'slides';
|
|
13
22
|
const DEFAULT_MODE = 'capture';
|
|
23
|
+
const DEFAULT_CAPTURE_RESOLUTION = '2160p';
|
|
14
24
|
const PDF_MODES = new Set(['capture', 'print']);
|
|
15
25
|
const SLIDE_FILE_PATTERN = /^slide-.*\.html$/i;
|
|
16
26
|
const FALLBACK_SLIDE_SIZE = { width: 960, height: 540 };
|
|
@@ -29,12 +39,14 @@ function printUsage() {
|
|
|
29
39
|
` --output <path> Output PDF path (default: ${DEFAULT_OUTPUT})`,
|
|
30
40
|
` --slides-dir <path> Slide directory (default: ${DEFAULT_SLIDES_DIR})`,
|
|
31
41
|
` --mode <mode> PDF export mode: capture|print (default: ${DEFAULT_MODE})`,
|
|
42
|
+
` --resolution <preset> Capture raster size preset: ${getResolutionChoices().join('|')}|4k (default: ${DEFAULT_CAPTURE_RESOLUTION}; ignored in print mode)`,
|
|
32
43
|
' -h, --help Show this help message',
|
|
33
44
|
'',
|
|
34
45
|
'Examples:',
|
|
35
46
|
' node scripts/html2pdf.js',
|
|
36
47
|
' node scripts/html2pdf.js --output dist/deck.pdf',
|
|
37
48
|
' node scripts/html2pdf.js --mode print --output dist/searchable.pdf',
|
|
49
|
+
' node scripts/html2pdf.js --resolution 2160p --output dist/deck-4k.pdf',
|
|
38
50
|
].join('\n'),
|
|
39
51
|
);
|
|
40
52
|
process.stdout.write('\n');
|
|
@@ -76,6 +88,25 @@ function cssPixelsToPdfPoints(value) {
|
|
|
76
88
|
return Math.round((normalizeDimension(value, 0) * PDF_POINTS_PER_INCH) / CSS_PIXELS_PER_INCH);
|
|
77
89
|
}
|
|
78
90
|
|
|
91
|
+
async function normalizeCaptureRasterSize(pngBytes, resolution = '') {
|
|
92
|
+
const targetSize = getResolutionSize(resolution);
|
|
93
|
+
if (!targetSize) {
|
|
94
|
+
return pngBytes;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const metadata = await sharp(pngBytes).metadata();
|
|
98
|
+
const currentWidth = normalizeDimension(metadata.width, targetSize.width);
|
|
99
|
+
const currentHeight = normalizeDimension(metadata.height, targetSize.height);
|
|
100
|
+
if (currentWidth === targetSize.width && currentHeight === targetSize.height) {
|
|
101
|
+
return pngBytes;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return sharp(pngBytes)
|
|
105
|
+
.resize(targetSize.width, targetSize.height, { fit: 'fill' })
|
|
106
|
+
.png()
|
|
107
|
+
.toBuffer();
|
|
108
|
+
}
|
|
109
|
+
|
|
79
110
|
function formatDiagnosticEntry(entry) {
|
|
80
111
|
const prefix = entry.slideFile ? `${entry.slideFile}: ` : '';
|
|
81
112
|
return `${prefix}${entry.message}`;
|
|
@@ -108,6 +139,7 @@ export function parseCliArgs(args) {
|
|
|
108
139
|
output: DEFAULT_OUTPUT,
|
|
109
140
|
slidesDir: DEFAULT_SLIDES_DIR,
|
|
110
141
|
mode: DEFAULT_MODE,
|
|
142
|
+
resolution: DEFAULT_CAPTURE_RESOLUTION,
|
|
111
143
|
help: false,
|
|
112
144
|
};
|
|
113
145
|
|
|
@@ -152,6 +184,17 @@ export function parseCliArgs(args) {
|
|
|
152
184
|
continue;
|
|
153
185
|
}
|
|
154
186
|
|
|
187
|
+
if (arg === '--resolution') {
|
|
188
|
+
options.resolution = normalizeResolutionPreset(readOptionValue(args, i, '--resolution'));
|
|
189
|
+
i += 1;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (arg.startsWith('--resolution=')) {
|
|
194
|
+
options.resolution = normalizeResolutionPreset(arg.slice('--resolution='.length));
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
|
|
155
198
|
throw new Error(`Unknown option: ${arg}`);
|
|
156
199
|
}
|
|
157
200
|
|
|
@@ -165,6 +208,10 @@ export function parseCliArgs(args) {
|
|
|
165
208
|
options.output = options.output.trim();
|
|
166
209
|
options.slidesDir = options.slidesDir.trim();
|
|
167
210
|
options.mode = normalizeMode(options.mode);
|
|
211
|
+
options.resolution = normalizeResolutionPreset(options.resolution);
|
|
212
|
+
if (options.mode === 'print') {
|
|
213
|
+
options.resolution = '';
|
|
214
|
+
}
|
|
168
215
|
|
|
169
216
|
return options;
|
|
170
217
|
}
|
|
@@ -188,13 +235,18 @@ export function buildPdfOptions(widthPx, heightPx) {
|
|
|
188
235
|
};
|
|
189
236
|
}
|
|
190
237
|
|
|
191
|
-
export function buildPageOptions(mode = DEFAULT_MODE) {
|
|
238
|
+
export function buildPageOptions(mode = DEFAULT_MODE, resolution = '') {
|
|
239
|
+
const targetResolution = normalizeMode(mode) === 'capture' ? getResolutionSize(resolution) : null;
|
|
192
240
|
return {
|
|
193
241
|
viewport: {
|
|
194
242
|
width: FALLBACK_SLIDE_SIZE.width,
|
|
195
243
|
height: FALLBACK_SLIDE_SIZE.height,
|
|
196
244
|
},
|
|
197
|
-
deviceScaleFactor: normalizeMode(mode) === 'capture'
|
|
245
|
+
deviceScaleFactor: normalizeMode(mode) === 'capture'
|
|
246
|
+
? targetResolution
|
|
247
|
+
? targetResolution.height / FALLBACK_SLIDE_SIZE.height
|
|
248
|
+
: DEFAULT_CAPTURE_DEVICE_SCALE_FACTOR
|
|
249
|
+
: 1,
|
|
198
250
|
};
|
|
199
251
|
}
|
|
200
252
|
|
|
@@ -470,6 +522,7 @@ export async function renderSlideToPdf(page, slideFile, slidesDir, options = {})
|
|
|
470
522
|
const slidePath = join(slidesDir, slideFile);
|
|
471
523
|
const slideUrl = pathToFileURL(slidePath).href;
|
|
472
524
|
const mode = normalizeMode(options.mode ?? DEFAULT_MODE);
|
|
525
|
+
const captureResolution = mode === 'capture' ? normalizeResolutionPreset(options.resolution ?? '') : '';
|
|
473
526
|
|
|
474
527
|
await page.goto(slideUrl, { waitUntil: 'load' });
|
|
475
528
|
await waitForSlideRenderReady(page, options);
|
|
@@ -495,11 +548,12 @@ export async function renderSlideToPdf(page, slideFile, slidesDir, options = {})
|
|
|
495
548
|
height: viewportSize.height,
|
|
496
549
|
},
|
|
497
550
|
});
|
|
551
|
+
const normalizedPngBytes = await normalizeCaptureRasterSize(pngBytes, captureResolution);
|
|
498
552
|
return {
|
|
499
553
|
mode,
|
|
500
554
|
width: normalizedSlideFrame.width,
|
|
501
555
|
height: normalizedSlideFrame.height,
|
|
502
|
-
pngBytes,
|
|
556
|
+
pngBytes: normalizedPngBytes,
|
|
503
557
|
};
|
|
504
558
|
}
|
|
505
559
|
|
|
@@ -560,7 +614,7 @@ async function main() {
|
|
|
560
614
|
}
|
|
561
615
|
|
|
562
616
|
const browser = await chromium.launch({ headless: true });
|
|
563
|
-
const page = await browser.newPage(buildPageOptions(options.mode));
|
|
617
|
+
const page = await browser.newPage(buildPageOptions(options.mode, options.resolution));
|
|
564
618
|
const diagnostics = createSlideDiagnostics();
|
|
565
619
|
diagnostics.attach(page);
|
|
566
620
|
const renderedSlides = [];
|
|
@@ -569,7 +623,10 @@ async function main() {
|
|
|
569
623
|
for (const slideFile of slideFiles) {
|
|
570
624
|
diagnostics.beginSlide(slideFile);
|
|
571
625
|
try {
|
|
572
|
-
const slideResult = await renderSlideToPdf(page, slideFile, slidesDir, {
|
|
626
|
+
const slideResult = await renderSlideToPdf(page, slideFile, slidesDir, {
|
|
627
|
+
mode: options.mode,
|
|
628
|
+
resolution: options.resolution,
|
|
629
|
+
});
|
|
573
630
|
renderedSlides.push(slideResult);
|
|
574
631
|
} catch (error) {
|
|
575
632
|
throw decorateError(error, slideFile, diagnostics.getSlideDiagnostics(slideFile));
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const RESOLUTION_PRESETS = Object.freeze({
|
|
2
|
+
'720p': { width: 1280, height: 720 },
|
|
3
|
+
'1080p': { width: 1920, height: 1080 },
|
|
4
|
+
'1440p': { width: 2560, height: 1440 },
|
|
5
|
+
'2160p': { width: 3840, height: 2160 },
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
const RESOLUTION_ALIASES = Object.freeze({
|
|
9
|
+
'4k': '2160p',
|
|
10
|
+
uhd: '2160p',
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
function getResolutionChoices() {
|
|
14
|
+
return Object.keys(RESOLUTION_PRESETS);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function normalizeResolutionPreset(value, options = {}) {
|
|
18
|
+
const { allowEmpty = true } = options;
|
|
19
|
+
|
|
20
|
+
if (typeof value !== 'string') {
|
|
21
|
+
if (allowEmpty) {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
throw new Error(`--resolution must be one of: ${getResolutionChoices().join(', ')}, 4k`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const trimmed = value.trim().toLowerCase();
|
|
28
|
+
if (trimmed === '') {
|
|
29
|
+
if (allowEmpty) {
|
|
30
|
+
return '';
|
|
31
|
+
}
|
|
32
|
+
throw new Error(`--resolution must be one of: ${getResolutionChoices().join(', ')}, 4k`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const normalized = RESOLUTION_ALIASES[trimmed] || trimmed;
|
|
36
|
+
if (!RESOLUTION_PRESETS[normalized]) {
|
|
37
|
+
throw new Error(`Unknown resolution "${value}". Expected one of: ${getResolutionChoices().join(', ')}, 4k`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return normalized;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getResolutionSize(value) {
|
|
44
|
+
const normalized = normalizeResolutionPreset(value);
|
|
45
|
+
if (!normalized) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const preset = RESOLUTION_PRESETS[normalized];
|
|
50
|
+
return { width: preset.width, height: preset.height };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = {
|
|
54
|
+
RESOLUTION_PRESETS,
|
|
55
|
+
getResolutionChoices,
|
|
56
|
+
getResolutionSize,
|
|
57
|
+
normalizeResolutionPreset,
|
|
58
|
+
};
|