diff --git a/news/changelog-1.9.md b/news/changelog-1.9.md index 441d8d590d..617aed79ee 100644 --- a/news/changelog-1.9.md +++ b/news/changelog-1.9.md @@ -74,6 +74,10 @@ All changes included in 1.9: - ([#13722](https://github.com/quarto-dev/quarto-cli/issues/13722)): Fix `light-content` / `dark-content` SCSS rules not included in Reveal.js format. (author: @mcanouil) +### `ipynb` + +- ([#13956](https://github.com/quarto-dev/quarto-cli/issues/13956)): Fix crash when rendering to `ipynb` format with figure labels (`#| label: fig-*`) on cells for cross references. + ## Projects - ([#13892](https://github.com/quarto-dev/quarto-cli/issues/13892)): Fix `output-dir: ./` deleting entire project directory. `output-dir` must be a subdirectory of the project directory and check is now better to avoid deleting the project itself when it revolves to the same path. diff --git a/src/resources/filters/customnodes/floatreftarget.lua b/src/resources/filters/customnodes/floatreftarget.lua index 38d8a70379..f1dffaf7e0 100644 --- a/src/resources/filters/customnodes/floatreftarget.lua +++ b/src/resources/filters/customnodes/floatreftarget.lua @@ -933,10 +933,9 @@ end, function(float) float.identifier) end - return pandoc.Div({ - float.content, - pandoc.Para(quarto.utils.as_inlines(float.caption_long) or {}), - }); + local blocks = pandoc.Blocks(float.content) + blocks:insert(pandoc.Para(quarto.utils.as_inlines(float.caption_long) or {})) + return pandoc.Div(blocks) end) -- this should really be "_quarto.format.isEmbedIpynb()" or something like that.. diff --git a/tests/docs/smoke-all/2026/01/27/issue-13956.qmd b/tests/docs/smoke-all/2026/01/27/issue-13956.qmd new file mode 100644 index 0000000000..8de340633e --- /dev/null +++ b/tests/docs/smoke-all/2026/01/27/issue-13956.qmd @@ -0,0 +1,23 @@ +--- +title: "Issue 13956" +format: ipynb +_quarto: + tests: + ipynb: + ensureIpynbCellMatches: + cellType: markdown + matches: + - 'Figure\s1' + - '#fig-test' + - 'quarto-xref' + - 'This should render without crashing' +--- + +Testing crash when using inline `#| label: fig-*` syntax with format: ipynb. + +```{python} +#| label: fig-test +print("This should render without crashing") +``` + +See @fig-test for the output. diff --git a/tests/smoke/smoke-all.test.ts b/tests/smoke/smoke-all.test.ts index b209c2a631..36650b9266 100644 --- a/tests/smoke/smoke-all.test.ts +++ b/tests/smoke/smoke-all.test.ts @@ -23,6 +23,7 @@ import { ensureDocxXpath, ensureFileRegexMatches, ensureHtmlElements, + ensureIpynbCellMatches, ensurePdfRegexMatches, ensurePdfTextPositions, ensurePdfMetadata, @@ -181,6 +182,7 @@ function resolveTestSpecs( ensureHtmlElementContents, ensureHtmlElementCount, ensureFileRegexMatches, + ensureIpynbCellMatches, ensureLatexFileRegexMatches, ensureTypstFileRegexMatches, ensureDocxRegexMatches, diff --git a/tests/verify.ts b/tests/verify.ts index d4ae29ada7..ca9db28cc8 100644 --- a/tests/verify.ts +++ b/tests/verify.ts @@ -302,6 +302,40 @@ export const validJsonWithFields = (file: string, fields: Record { + const { cellType, matches = [], noMatches = [] } = options; + return { + name: `IPYNB ${file} has ${cellType} cells matching patterns`, + verify: async (_output: ExecuteOutput[]) => { + const jsonStr = Deno.readTextFileSync(file); + const notebook = JSON.parse(jsonStr); + // deno-lint-ignore no-explicit-any + const cells = notebook.cells.filter((c: any) => c.cell_type === cellType); + // deno-lint-ignore no-explicit-any + const content = cells.map((c: any) => + Array.isArray(c.source) ? c.source.join("") : c.source + ).join("\n"); + + for (const m of matches) { + const regex = typeof m === "string" ? new RegExp(m) : m; + assert(regex.test(content), `Pattern ${m} not found in ${cellType} cells of ${file}`); + } + for (const m of noMatches) { + const regex = typeof m === "string" ? new RegExp(m) : m; + assert(!regex.test(content), `Pattern ${m} should not be in ${cellType} cells of ${file}`); + } + return Promise.resolve(); + } + }; +}; + export const outputCreated = ( input: string, to: string,