HTML preview: drop embed-resources and clear stale deck on failure
Build and Deploy Verso / deploy (push) Successful in 12m5s

Two HTML/RevealJS preview fixes:

1. Stop passing --embed-resources to quarto render. A self-contained
   single-file HTML breaks reveal.js plugins that load/store resources at
   runtime (chalkboard, multiplex) and is slow to transfer. Quarto now
   emits the HTML plus a sibling "<basename>_files/" asset dir referenced
   by relative paths; both are served from the same .../output/ path
   (nginx output/(.+) and web :file(.*) both capture slashes), so the
   relative links resolve. The renamed output.html still points at the
   unchanged "<basename>_files" dir. This also fixes the slow-load issue,
   since assets now load on demand instead of one giant inlined file.

2. On a failed compile that follows a successful one, the previous deck
   stayed in the iframe, making the failure look like a success. We now
   clear pdfFile when a non-success status carries a stale output.html.
   The last-good-PDF-beside-the-error behaviour is preserved for PDF
   output (only output.html is dropped).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
claude
2026-06-01 08:40:18 +00:00
parent 56d66b109e
commit e87bbfe5b0
2 changed files with 26 additions and 10 deletions
+17 -10
View File
@@ -54,21 +54,28 @@ function _buildQuartoCommand(mainFile) {
//
// 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.
//
// We do NOT pass --embed-resources. A self-contained single-file HTML
// breaks reveal.js plugins that load/store resources at runtime (e.g.
// chalkboard, multiplex) and is slow to transfer. Instead Quarto emits
// the HTML plus a sibling "<basename>_files/" asset directory; the HTML
// references it with relative paths. Both the html and the asset dir are
// served from the same .../output/ path, so the relative links resolve.
//
// After render we rename the produced top-level file to output.pdf or
// output.html. The asset directory keeps its "<basename>_files" name; the
// renamed output.html still points at it via the unchanged relative refs.
//
// The extension merge (cp -rn, no-clobber so user extensions win) and the
// trailing semicolon (so a missing /opt/quarto-extensions doesn't abort)
// are kept. mv uses relative paths because LocalCommandRunner.replace()
// only substitutes the FIRST $COMPILE_DIR and the shell CWD is the dir.
const inputPath = `$COMPILE_DIR/${mainFile}`
const baseName = mainFile.replace(/\.[^/.]+$/, '') // strip extension
// LocalCommandRunner.replace() only replaces the FIRST $COMPILE_DIR
// occurrence in the shell string, so the mv commands use relative paths
// instead — the shell CWD is already set to the compile directory.
// Merge pre-installed extensions into the compile dir before rendering.
// -n (no-clobber) ensures project-uploaded extensions take precedence.
// The semicolon means a missing /opt/quarto-extensions dir doesn't abort.
const cmd =
`mkdir -p _extensions && ` +
`cp -rn /opt/quarto-extensions/_extensions/. _extensions/ 2>/dev/null; ` +
`quarto render ${inputPath} --embed-resources 2>&1 && ` +
`quarto render ${inputPath} 2>&1 && ` +
`(mv ${baseName}.pdf output.pdf 2>/dev/null || ` +
`mv ${baseName}.html output.html 2>/dev/null)`
return ['/bin/sh', '-c', cmd]
@@ -505,6 +505,15 @@ export const LocalCompileProvider: FC<React.PropsWithChildren> = ({
// set the PDF context
if (data.status === 'success') {
setPdfFile(handleOutputFiles(outputFiles, projectId, data))
} else {
// For PDF output we intentionally keep the last good PDF visible
// next to the error log (standard Overleaf behaviour). But for an
// HTML deck rendered in an iframe, leaving the previous deck up is
// confusing — it looks like the failed compile succeeded. So drop
// a stale HTML output on any non-success status.
setPdfFile(prev =>
prev?.path === 'output.html' ? undefined : prev
)
}
setFileList(buildFileList(outputFiles, data))