diff --git a/news/changelog-1.11.md b/news/changelog-1.11.md index 4e98d750868..06d62dd6e30 100644 --- a/news/changelog-1.11.md +++ b/news/changelog-1.11.md @@ -81,6 +81,7 @@ All changes included in 1.11: - ([#15125](https://github.com/quarto-dev/quarto-cli/pull/15125)): Fix Google Scholar metadata failing the render with a CSL JSON (`.json`) bibliography. - ([#15128](https://github.com/quarto-dev/quarto-cli/pull/15128)): Fix Google Scholar metadata missing references with a YAML bibliography not wrapped in `---` delimiters. - ([#15131](https://github.com/quarto-dev/quarto-cli/issues/15131)): Fix the light/dark toggle appearing, with no dark styles to switch to, when the `dark` brand file holds only comments and is ignored. +- ([#15207](https://github.com/quarto-dev/quarto-cli/issues/15207)): Fix the BibTeX citation in the HTML appendix showing a copy button that does nothing when `code-copy: false` is set. ### `pdf` diff --git a/src/format/html/format-html-appendix.ts b/src/format/html/format-html-appendix.ts index 26f5813683d..7560451955d 100644 --- a/src/format/html/format-html-appendix.ts +++ b/src/format/html/format-html-appendix.ts @@ -26,6 +26,7 @@ import { import { createCodeBlock, createCodeCopyButton, + formatHasCodeCopy, hasMarginCites, hasMarginRefs, insertFootnotesTitle, @@ -306,13 +307,18 @@ export async function processDocumentAppendix( const bibTexDiv = createCodeBlock(doc, cite.bibtex, "bibtex"); bibTexDiv.classList.add(kQuartoCiteBibtexClass); - const outerScaffold = doc.createElement("div"); - outerScaffold.classList.add("code-copy-outer-scaffold"); - outerScaffold.appendChild(bibTexDiv); - contentsDiv.appendChild(outerScaffold); + if (formatHasCodeCopy(format)) { + bibTexDiv.classList.add("code-with-copy"); + const outerScaffold = doc.createElement("div"); + outerScaffold.classList.add("code-copy-outer-scaffold"); + outerScaffold.appendChild(bibTexDiv); + contentsDiv.appendChild(outerScaffold); - const copyButton = createCodeCopyButton(doc, format); - outerScaffold.appendChild(copyButton); + const copyButton = createCodeCopyButton(doc, format); + outerScaffold.appendChild(copyButton); + } else { + contentsDiv.appendChild(bibTexDiv); + } } if (cite?.html) { diff --git a/src/format/html/format-html-shared.ts b/src/format/html/format-html-shared.ts index e9f31e4f12f..30456afc928 100644 --- a/src/format/html/format-html-shared.ts +++ b/src/format/html/format-html-shared.ts @@ -28,6 +28,8 @@ import { import { formatResourcePath } from "../../core/resources.ts"; import { Document, Element } from "../../core/deno-dom.ts"; import { normalizePath } from "../../core/path.ts"; +import { formatHasBootstrap } from "./format-html-info.ts"; +import { HtmlFormatFeatureDefaults } from "./format-html-types.ts"; // features that are enabled by default for 'html'. setting // all of these to false will yield the minimal html output @@ -73,6 +75,33 @@ export const kCitation = "citation"; export const kDocumentCss = "document-css"; export const kBootstrapDependencyName = "bootstrap"; +export function htmlFormatFeatureDefaults( + format: Format, +): HtmlFormatFeatureDefaults { + const bootstrap = formatHasBootstrap(format); + const minimal = format.metadata[kMinimal] === true; + return { + tabby: !minimal && !bootstrap, + copyCode: !minimal, + anchors: !minimal, + hoverCitations: !minimal, + hoverFootnotes: !minimal, + figResponsive: !minimal, + codeAnnotations: !minimal, + hoverXrefs: !minimal, + }; +} + +// whether code copy buttons (and clipboard.js) are enabled for this format +export function formatHasCodeCopy( + format: Format, + enabledByDefault = htmlFormatFeatureDefaults(format).copyCode, +): boolean { + return enabledByDefault + ? format.metadata[kCodeCopy] !== false + : !!format.metadata[kCodeCopy]; +} + export const clipboardDependency = () => { const dependency: FormatDependency = { name: "clipboard" }; dependency.scripts = []; @@ -492,7 +521,6 @@ export function createCodeBlock( ) { const preEl = doc.createElement("PRE"); preEl.classList.add("sourceCode"); - preEl.classList.add("code-with-copy"); const codeEl = doc.createElement("CODE"); codeEl.classList.add("sourceCode"); diff --git a/src/format/html/format-html.ts b/src/format/html/format-html.ts index aaf3e38cb4c..e78a60a1a85 100644 --- a/src/format/html/format-html.ts +++ b/src/format/html/format-html.ts @@ -67,12 +67,13 @@ import { bootstrapExtras } from "./format-html-bootstrap.ts"; import { clipboardDependency, createCodeCopyButton, + formatHasCodeCopy, + htmlFormatFeatureDefaults, kAnchorSections, kAxe, kBootstrapDependencyName, kCitationsHover, kCodeAnnotations, - kCodeCopy, kComments, kDocumentCss, kFootnotesHover, @@ -306,11 +307,7 @@ export async function htmlFormatExtras( } else { options.tabby = format.metadata[kTabsets] || false; } - if (featureDefaults.copyCode) { - options.copyCode = format.metadata[kCodeCopy] !== false; - } else { - options.copyCode = format.metadata[kCodeCopy] || false; - } + options.copyCode = formatHasCodeCopy(format, featureDefaults.copyCode); if (featureDefaults.anchors) { options.anchors = format.metadata[kAnchorSections] !== false; } else { @@ -682,23 +679,6 @@ function htmlFormatFilterParams(format: Format) { }; } -function htmlFormatFeatureDefaults( - format: Format, -): HtmlFormatFeatureDefaults { - const bootstrap = formatHasBootstrap(format); - const minimal = format.metadata[kMinimal] === true; - return { - tabby: !minimal && !bootstrap, - copyCode: !minimal, - anchors: !minimal, - hoverCitations: !minimal, - hoverFootnotes: !minimal, - figResponsive: !minimal, - codeAnnotations: !minimal, - hoverXrefs: !minimal, - }; -} - function htmlFormatPostprocessor( format: Format, featureDefaults?: HtmlFormatFeatureDefaults, @@ -712,9 +692,7 @@ function htmlFormatPostprocessor( } // read options - const codeCopy = featureDefaults.copyCode - ? format.metadata[kCodeCopy] !== false - : format.metadata[kCodeCopy] || false; + const codeCopy = formatHasCodeCopy(format, featureDefaults.copyCode); const anchors = featureDefaults.anchors ? format.metadata[kAnchorSections] !== false diff --git a/tests/docs/smoke-all/2026/10/27/issue-15207.qmd b/tests/docs/smoke-all/2026/10/27/issue-15207.qmd new file mode 100644 index 00000000000..b0ae9baea91 --- /dev/null +++ b/tests/docs/smoke-all/2026/10/27/issue-15207.qmd @@ -0,0 +1,19 @@ +--- +title: "Citation BibTeX block without code-copy" +author: "Author name" +date: "2025-01-01" +format: html +citation: true +code-copy: false +_quarto: + tests: + html: + ensureHtmlElements: + - + - '#quarto-citation pre.quarto-appendix-bibtex' + - + - '#quarto-citation button.code-copy-button' + - '#quarto-citation div.code-copy-outer-scaffold' +--- + +With `code-copy: false`, clipboard.js is not loaded, so a copy button on the BibTeX block in the citation appendix would do nothing. The BibTeX block is still rendered, but without the copy button or its scaffold, the same as other code blocks in the document (#15207).