From 6c284bb5c341b4fc45ec341e1545106ded15d9ef Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 27 Jan 2026 21:47:06 +0100 Subject: [PATCH 1/3] test, ipynb - add a test for cross ref in ipynb Also add a verify function for Ipynb cell content --- .../docs/smoke-all/2026/01/27/issue-13956.qmd | 23 +++++++++++++ tests/smoke/smoke-all.test.ts | 2 ++ tests/verify.ts | 34 +++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 tests/docs/smoke-all/2026/01/27/issue-13956.qmd 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, From a6d1cf23bc51ad2f195f8226d19201e6db043ae0 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 27 Jan 2026 22:04:07 +0100 Subject: [PATCH 2/3] Fix ipynb crash with inline figure labels Wrap float.content with pandoc.Blocks() in the ipynb renderer to handle content that arrives as a list instead of a typed Blocks element. Fixes #13956 --- src/resources/filters/customnodes/floatreftarget.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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.. From 7cbfb86417a9d3fb686a3a748e9cfbc19d20b3ea Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 27 Jan 2026 22:18:40 +0100 Subject: [PATCH 3/3] Add to changelog --- news/changelog-1.9.md | 4 ++++ 1 file changed, 4 insertions(+) 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.