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 (
+
+ )
+ }
+
switch (pdfViewer) {
case 'native':
return
diff --git a/services/web/frontend/js/features/pdf-preview/util/output-files.ts b/services/web/frontend/js/features/pdf-preview/util/output-files.ts
index f869311d29..ab27bd3188 100644
--- a/services/web/frontend/js/features/pdf-preview/util/output-files.ts
+++ b/services/web/frontend/js/features/pdf-preview/util/output-files.ts
@@ -24,7 +24,9 @@ export function handleOutputFiles(
projectId: string,
data: CompileResponseData
): PDFFile | null {
- const outputFile = outputFiles.get('output.pdf')
+ // Accept either a PDF or an HTML output (e.g. RevealJS presentation)
+ const outputFile =
+ outputFiles.get('output.pdf') ?? outputFiles.get('output.html')
if (!outputFile) return null
outputFile.editorId = outputFile.editorId || EDITOR_SESSION_ID
@@ -54,7 +56,7 @@ export function handleOutputFiles(
params.set('popupDownload', 'true') // save PDF download as file
params.set('editorId', outputFile.editorId)
- outputFile.pdfDownloadUrl = `/download/project/${projectId}/build/${outputFile.build}/output/output.pdf?${params}`
+ outputFile.pdfDownloadUrl = `/download/project/${projectId}/build/${outputFile.build}/output/${outputFile.path}?${params}`
}
return outputFile