feat(pdf): three-level quality (Normal/Compressed/Draft) + download modal
Build and Deploy Verso / deploy (push) Successful in 11m30s

- compilePdfQuality: 'none' | 'ebook' | 'screen' replaces compressPdf: boolean
  throughout the stack (types, context, compiler, RequestParser, CompileManager)
- Compile dropdown now shows Normal / Compressed / Draft under "PDF quality"
  (ebook=150dpi, screen=72dpi via Ghostscript)
- Download button opens a modal with the same three quality options plus
  per-option descriptions and a Ghostscript attribution note; compressed
  downloads are served by a new /download/.../output.pdf/compressed endpoint
  that fetches the last build from clsi and pipes it through GS on demand

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
claude
2026-07-25 12:36:10 +00:00
co-authored by Claude Sonnet 4.6
parent e5becc560c
commit 080de97632
13 changed files with 346 additions and 90 deletions
+5 -4
View File
@@ -318,8 +318,8 @@ async function doCompile(request, stats, timings) {
'done compile'
)
if (request.compressPdf) {
await _compressOutputPdf(compileDir)
if (request.compilePdfQuality && request.compilePdfQuality !== 'none') {
await _compressOutputPdf(compileDir, request.compilePdfQuality)
}
const { outputFiles, buildId } = await _saveOutputFiles({
@@ -449,7 +449,8 @@ async function clearExpiredProjects(maxCacheAgeMs) {
}
}
async function _compressOutputPdf(compileDir) {
async function _compressOutputPdf(compileDir, quality) {
const preset = quality === 'screen' ? '/screen' : '/ebook'
const src = Path.join(compileDir, 'output.pdf')
const tmp = Path.join(compileDir, 'output-gs-tmp.pdf')
const t0 = Date.now()
@@ -461,7 +462,7 @@ async function _compressOutputPdf(compileDir) {
'-q', '-dNOPAUSE', '-dBATCH',
'-sDEVICE=pdfwrite',
'-dCompatibilityLevel=1.7',
'-dPDFSETTINGS=/ebook',
`-dPDFSETTINGS=${preset}`,
`-sOutputFile=${tmp}`,
src,
],
+4 -4
View File
@@ -91,10 +91,10 @@ function parse(body, callback) {
default: false,
type: 'boolean',
})
response.compressPdf = _parseAttribute(
'compressPdf',
compile.options.compressPdf,
{ default: false, type: 'boolean' }
response.compilePdfQuality = _parseAttribute(
'compilePdfQuality',
compile.options.compilePdfQuality,
{ default: 'none', type: 'string' }
)
response.stopOnFirstError = _parseAttribute(
'stopOnFirstError',