From 48fd24a6b2ea17549a0a7bc06f71412082cc99b1 Mon Sep 17 00:00:00 2001 From: claude Date: Sun, 31 May 2026 15:32:00 +0000 Subject: [PATCH] Add HTML/RevealJS preview alongside existing PDF preview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit clsi-nginx.conf: the types{} block was overriding all nginx defaults, leaving HTML/CSS/JS/fonts as application/octet-stream. Add the full set of web MIME types so RevealJS assets are served correctly. Also needed for X-Content-Type-Options: nosniff to pass. CompileController.js: success was hardcoded to require output.pdf. Also accept output.html so a RevealJS compile is reported as 'success' rather than 'failure'. QuartoRunner.js: remove hardcoded --to typst --output output.pdf. Instead run `quarto render` without --to/--output so the YAML frontmatter decides the format (typst → PDF, revealjs → HTML, etc.). Pass --embed-resources so HTML output is self-contained (flag is silently ignored by the typst backend). After render, rename main.pdf → output.pdf or main.html → output.html so the pipeline finds the standard canonical filename. output-files.ts: handleOutputFiles now falls back to output.html when output.pdf is absent. Download URL uses outputFile.path instead of the hardcoded 'output.pdf' string. pdf-viewer.tsx: when pdfUrl contains output.html, bypass PDF.js entirely and render a sandboxed iframe (allow-scripts for RevealJS interactivity, allow-presentation for fullscreen). Usage: set `format: revealjs` in the .qmd YAML frontmatter to get an HTML presentation preview; set `format: typst` for PDF. Co-Authored-By: Claude Sonnet 4.6 --- server-ce/nginx/clsi-nginx.conf | 15 +++++++++++++-- services/clsi/app/js/CompileController.js | 5 ++++- services/clsi/app/js/QuartoRunner.js | 16 +++++++++++++--- .../pdf-preview/components/pdf-viewer.tsx | 13 +++++++++++++ .../js/features/pdf-preview/util/output-files.ts | 6 ++++-- 5 files changed, 47 insertions(+), 8 deletions(-) diff --git a/server-ce/nginx/clsi-nginx.conf b/server-ce/nginx/clsi-nginx.conf index 5ce22a3f56..b907ecfcce 100644 --- a/server-ce/nginx/clsi-nginx.conf +++ b/server-ce/nginx/clsi-nginx.conf @@ -25,8 +25,19 @@ server { gzip_types text/plain; gzip_proxied any; types { - text/plain log blg aux stdout stderr; - application/pdf pdf; + text/html html htm; + text/css css; + application/javascript js; + application/json json; + image/svg+xml svg svgz; + image/png png; + image/jpeg jpeg jpg; + image/gif gif; + image/webp webp; + font/woff woff; + font/woff2 woff2; + application/pdf pdf; + text/plain log blg aux stdout stderr txt; } # handle output files for specific users location ~ ^/project/([0-9a-f]+)/user/([0-9a-f]+)/build/([0-9a-f-]+)/output/(.+)$ { diff --git a/services/clsi/app/js/CompileController.js b/services/clsi/app/js/CompileController.js index 53925c1291..9545d900a2 100644 --- a/services/clsi/app/js/CompileController.js +++ b/services/clsi/app/js/CompileController.js @@ -90,7 +90,10 @@ function compile(req, res, next) { } else { if ( outputFiles.some( - file => file.path === 'output.pdf' && file.size > 0 + file => + (file.path === 'output.pdf' || + file.path === 'output.html') && + file.size > 0 ) ) { status = 'success' diff --git a/services/clsi/app/js/QuartoRunner.js b/services/clsi/app/js/QuartoRunner.js index c548861b20..f54048f0cd 100644 --- a/services/clsi/app/js/QuartoRunner.js +++ b/services/clsi/app/js/QuartoRunner.js @@ -50,11 +50,21 @@ function runQuarto(compileName, options, callback) { function _buildQuartoCommand(mainFile) { // Run through a POSIX shell so stderr is merged into stdout (2>&1). - // Quarto writes all progress and error messages to stderr; without this - // the log panel would be empty on failure. // LocalCommandRunner replaces $COMPILE_DIR before the shell sees it. + // + // We do NOT pass --to or --output: let the YAML frontmatter decide the + // output format (typst → output.pdf, revealjs → output.html, etc.). + // --embed-resources makes HTML output self-contained (ignored for typst). + // After render we rename the produced file to the canonical output name + // expected by the rest of the pipeline. const inputPath = `$COMPILE_DIR/${mainFile}` - const cmd = `quarto render ${inputPath} --to typst --output output.pdf 2>&1` + const baseName = mainFile.replace(/\.[^/.]+$/, '') // strip extension + const pdfOut = `$COMPILE_DIR/${baseName}.pdf` + const htmlOut = `$COMPILE_DIR/${baseName}.html` + const cmd = + `quarto render ${inputPath} --embed-resources 2>&1 && ` + + `(mv ${pdfOut} $COMPILE_DIR/output.pdf 2>/dev/null || ` + + `mv ${htmlOut} $COMPILE_DIR/output.html 2>/dev/null)` return ['/bin/sh', '-c', cmd] } diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-viewer.tsx b/services/web/frontend/js/features/pdf-preview/components/pdf-viewer.tsx index c4d79243b4..657ad95cbb 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-viewer.tsx +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-viewer.tsx @@ -12,6 +12,19 @@ function PdfViewer() { return null } + // HTML outputs (RevealJS, etc.) must always use the native iframe; + // PDF.js cannot render HTML. + if (pdfUrl.includes('output.html')) { + return ( +