Skip to content

Commit 8a62c54

Browse files
authored
Merge branch 'main' into chore/pandoc-3.6
2 parents 1c221f5 + 7071969 commit 8a62c54

File tree

74 files changed

+2602
-1994
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+2602
-1994
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,25 @@
22

33
# Quarto
44

5-
Quarto is an open-source scientific and technical publishing system built on [Pandoc](https://pandoc.org). Quarto documents are authored using [markdown](https://en.wikipedia.org/wiki/Markdown), an easy to write plain text format.
5+
Quarto is an open-source scientific and technical publishing system built on [Pandoc](https://pandoc.org). Quarto documents are authored using [Markdown](https://en.wikipedia.org/wiki/Markdown), an easy to write plain text format.
66

77
In addition to the core capabilities of Pandoc, Quarto includes:
88

99
1. Embedding code and output from Python, R, Julia, and JavaScript via integration with [Jupyter](https://jupyter.org/), [Knitr](https://yihui.org/knitr/), and [Observable](https://github.com/observablehq/).
1010

11-
2. A variety of extensions to Pandoc markdown useful for technical writing including cross-references, sub-figures, layout panels, hoverable citations and footnotes, callouts, and more.
11+
2. A variety of extensions to Pandoc Markdown useful for technical writing including cross-references, sub-figures, layout panels, hoverable citations and footnotes, callouts, and more.
1212

1313
3. A project system for rendering groups of documents at once, sharing options across documents, and producing aggregate output like [websites](https://quarto.org/docs/websites/) and [books](https://quarto.org/docs/books/).
1414

1515
4. Authoring using a wide variety of editors and notebooks including [JupyterLab](https://quarto.org/docs/tools/jupyter-lab.html), [RStudio](https://quarto.org/docs/tools/rstudio.html), and [VS Code](https://quarto.org/docs/tools/vscode.html).
1616

17-
5. A [visual markdown editor](https://quarto.org/docs/visual-editor/) that provides a productive writing interface for composing long-form documents.
17+
5. A [visual Markdown editor](https://quarto.org/docs/visual-editor/) that provides a productive writing interface for composing long-form documents.
1818

1919
Learn more about Quarto at <https://quarto.org>.
2020

2121
## Development Version
2222

23-
To install the development version of the Quarto CLI, clone the quarto-cli repository then run the configure script for your platform (`configure.sh` for Linux/macOS or `configure.cmd` for Windows). For example:
23+
To install the development version of the Quarto CLI, clone the `quarto-cli` repository then run the configure script for your platform (`configure.sh` for Linux/macOS or `configure.cmd` for Windows). For example:
2424

2525
```bash
2626
git clone https://github.com/quarto-dev/quarto-cli

news/changelog-1.7.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@ All changes included in 1.7:
99
- ([#11752](https://github.com/quarto-dev/quarto-cli/issues/11752)): Fix regression with non-alphanumeric characters in `categories` preventing correct filtering of listing.
1010
- ([#11561](https://github.com/quarto-dev/quarto-cli/issues/11561)): Fix a regression with `$border-color` that impacted, callouts borders, tabset borders, and table borders of the defaults themes. `$border-color` is now correctly a mixed of `$body-color` and `$body-bg` even for the default theme.
1111
- ([#11943](https://github.com/quarto-dev/quarto-cli/issues/11943)): Fix callout title color on dark theme in revealjs following Revealjs update in quarto 1.6.
12+
- ([#12147](https://github.com/quarto-dev/quarto-cli/issues/12147)): for RevealJS format, `serif` and `simple` themes defaults back to have their heading color (`$presentation-heading-color`) to be the same as the body color (`$body-color`) as in Quarto 1.5.
13+
- ([#11659](https://github.com/quarto-dev/quarto-cli/pull/11659)): Julia engine: Fix escaping bug where paths containing spaces or backslashes break server startup on Windows.
1214

1315
## YAML validation
1416

1517
- ([#11654](https://github.com/quarto-dev/quarto-cli/issues/11654)): Allow `page-inset` as value in `column` key for code cells.
18+
- ([#10251](https://github.com/quarto-dev/quarto-cli/issues/10251)): EJS template for website now uses proper object to get alt text for logo in sidebar.
19+
- ([#12151](https://github.com/quarto-dev/quarto-cli/issues/12151)): Fix YAML validation in computations cell on Windows.
20+
- ([#12151](https://github.com/quarto-dev/quarto-cli/pull/12151)): Basic YAML validation is now active in cell for document using Julia engine.
1621

1722
## Website projects
1823

@@ -32,6 +37,10 @@ All changes included in 1.7:
3237

3338
- ([#11608](https://github.com/quarto-dev/quarto-cli/pull/11608)): Do not issue error message when calling `quarto check info`.
3439

40+
## `quarto convert`
41+
42+
- ([#12042](https://github.com/quarto-dev/quarto-cli/issues/12042)): Preserve Markdown content that follows YAML metadata in a `raw` .ipynb cell.
43+
3544
## `html` format
3645

3746
- ([#11860](https://github.com/quarto-dev/quarto-cli/issues/11860)): ES6 modules that import other local JS modules in documents with `embed-resources: true` are now correctly embedded.
@@ -56,6 +65,10 @@ All changes included in 1.7:
5665
- Update to Pandoc's template following Pandoc 3.6.3 support:
5766
- `article.jats_publishing` partials now support `author.roles`
5867

68+
### Quarto PDF engine
69+
70+
- ([#12194](https://github.com/quarto-dev/quarto-cli/issues/12194)): More specific checks added in log parsing to automatically find missing fonts.
71+
5972
## `typst` format
6073

6174
- ([#11578](https://github.com/quarto-dev/quarto-cli/issues/11578)): Typst column layout widths use fractional `fr` units instead of percent `%` units for unitless and default widths in order to fill the enclosing block and not spill outside it.
@@ -84,8 +97,12 @@ All changes included in 1.7:
8497

8598
### `julia`
8699

87-
- ([#11659](https://github.com/quarto-dev/quarto-cli/pull/11659)): Fix escaping bug where paths containing spaces or backslashes break server startup on Windows.
88-
- ([#12121](https://github.com/quarto-dev/quarto-cli/pull/12121)): Update QuartoNotebookRunner to 0.13.1. Support for evaluating Python cells via [PythonCall.jl](https://github.com/JuliaPy/PythonCall.jl) added.
100+
- ([#12121](https://github.com/quarto-dev/quarto-cli/pull/12121)): Update QuartoNotebookRunner to 0.14.0. Support for evaluating Python cells via [PythonCall.jl](https://github.com/JuliaPy/PythonCall.jl) added. Support for notebook caching via `execute.cache` added.
101+
- ([#12151](https://github.com/quarto-dev/quarto-cli/pull/12151)): Basic YAML validation is now active for document using Julia engine.
102+
103+
### `jupyter`
104+
105+
- ([#12114](https://github.com/quarto-dev/quarto-cli/issues/12114)): `JUPYTERCACHE` environment variable from [Jupyter cache CLI](https://jupyter-cache.readthedocs.io/en/latest/using/cli.html) is now respected by Quarto when `cache: true` is used. This environment variable allows to change the path of the cache directory.
89106

90107
## Other Fixes and Improvements
91108

@@ -97,3 +114,4 @@ All changes included in 1.7:
97114
- ([#10532](https://github.com/quarto-dev/quarto-cli/issues/10532)): Quarto changed default of `--headless=old` to `--headless` as [Chrome 132 has removed old headless mode](https://developer.chrome.com/blog/removing-headless-old-from-chrome) and only support new mode. For using old mode, `QUARTO_CHROMIUM` could be set to a [new `chrome-headless-shell` binary](https://developer.chrome.com/blog/chrome-headless-shell) or too an older chrome version (between 128 and 132) and `QUARTO_CHROMIUM_HEADLESS_MODE` set to `old` for using old headless mode with that compatabitle version.
98115
- ([#10961](https://github.com/quarto-dev/quarto-cli/issues/10961)): Add more information on which Chrome Headless will be used in `quarto check install`. This is helpful to help debug mermaid issues.
99116
- ([#11951](https://github.com/quarto-dev/quarto-cli/issues/11951)): Raw LaTeX table without `tbl-` prefix label for using Quarto crossref are now correctly passed through unmodified.
117+
- ([#12117](https://github.com/quarto-dev/quarto-cli/issues/12117)): Color output to stdout and stderr is now correctly rendered for `html` format in the Jupyter and Julia engines.

package/src/common/patches/revealjs-theme-0001-simple.patch

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ index f0472d7b9..3b1e3cbdc 100644
2828
+$font-family-sans-serif: "Lato", sans-serif !default;
2929
+$body-color: #000 !default;
3030
+$presentation-heading-font: "News Cycle", sans-serif !default;
31-
+$presentation-heading-color: #000 !default;
31+
+$presentation-heading-color: $body-color !default;
3232
+$presentation-heading-text-shadow: none !default;
3333
+$presentation-heading-text-transform: none !default;
3434
+$body-bg: #fff !default;

package/src/common/patches/revealjs-theme-0001-sky.patch

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ index 62a52b782..2debff7bc 100644
2828
+$font-family-sans-serif: "Open Sans", sans-serif !default;
2929
+$body-color: #333 !default;
3030
+$presentation-heading-font: "Quicksand", sans-serif !default;
31-
+$presentation-heading-color: #333 !default;
31+
+$presentation-heading-color: $body-color !default;
3232
+$presentation-heading-letter-spacing: -0.08em !default;
3333
+$presentation-heading-text-shadow: none !default;
3434
+$body-bg: #f7fbfc !default;

src/command/convert/jupyter.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
jupyterCellSrcAsLines,
3131
jupyterCellSrcAsStr,
3232
} from "../../core/jupyter/jupyter-shared.ts";
33+
import { assert } from "testing/asserts";
3334

3435
export async function markdownToJupyterNotebook(
3536
file: string,
@@ -71,11 +72,15 @@ export async function jupyterNotebookToMarkdown(
7172
case "raw":
7273
// see if this is the front matter
7374
if (frontMatter === undefined) {
74-
frontMatter = partitionYamlFrontMatter(
75-
jupyterCellSrcAsStr(cell),
76-
)?.yaml;
77-
if (!frontMatter) {
78-
md.push(...mdFromRawCell(cellWithOptions));
75+
const { yaml: cellYaml, markdown: cellMarkdown } =
76+
partitionYamlFrontMatter(
77+
jupyterCellSrcAsStr(cell),
78+
) || {};
79+
if (cellYaml) {
80+
frontMatter = cellYaml;
81+
}
82+
if (cellMarkdown) {
83+
md.push(cellMarkdown);
7984
}
8085
} else {
8186
md.push(...mdFromRawCell(cellWithOptions));
@@ -130,14 +135,22 @@ export async function jupyterNotebookToMarkdown(
130135
}
131136
}
132137

138+
// if we found front matter, then the markdown source will start with enough
139+
// newlines for the front matter to have been detected in the first place.
140+
// So we only need to add newlines if there was no front matter.
141+
//
142+
// If this invariant breaks, we have a bug of some kind, so let's just assert it
143+
assert(frontMatter || !mdSource.match(/^\n\n/));
144+
const maybeYamlMdBreak = frontMatter ? "" : "\n\n";
145+
133146
// return yaml + markdown
134147
const yamlText = stringify(yaml, {
135148
indent: 2,
136149
lineWidth: -1,
137150
sortKeys: false,
138151
skipInvalid: true,
139152
});
140-
return `---\n${yamlText}---\n\n${mdSource}`;
153+
return `---\n${yamlText}---${maybeYamlMdBreak}${mdSource}`;
141154
}
142155

143156
async function mdFromCodeCell(

src/command/render/freeze.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,10 @@ export function freezeExecuteResult(
5353
result: ExecuteResult,
5454
) {
5555
// resolve includes within executeResult
56+
// nb: Beware to not modify the original result object
5657
const innerResult = {
5758
...result,
59+
includes: result.includes ? { ...result.includes } : undefined,
5860
} as ExecuteResult;
5961
const resolveIncludes = (
6062
name: "include-in-header" | "include-before-body" | "include-after-body",

src/command/render/latexmk/parse-error.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,11 @@ function findMissingFonts(dir: string): string[] {
209209
}
210210

211211
const formatFontFilter = (match: string, _text: string) => {
212-
const base = basename(match);
213-
return fontSearchTerm(base);
212+
// Remove special prefix / suffix e.g. 'file:HaranoAjiMincho-Regular.otf:-kern;jfm=ujis'
213+
// https://github.com/quarto-dev/quarto-cli/issues/12194
214+
const base = basename(match).replace(/^.*?:|:.*$/g, "");
215+
// return found file directly if it has an extension
216+
return /[.]/.test(base) ? base : fontSearchTerm(base);
214217
};
215218

216219
const estoPdfFilter = (_match: string, _text: string) => {
@@ -231,21 +234,20 @@ const packageMatchers = [
231234
regex: /.*!.+ error:.+\(file ([^)]+)\): .*/g,
232235
filter: formatFontFilter,
233236
},
234-
{
235-
regex: /.*Package widetext error: Install the ([^ ]+) package.*/g,
236-
filter: (match: string, _text: string) => {
237-
return `${match}.sty`;
238-
},
239-
},
240237
{
241238
regex: /.*Unable to find TFM file "([^"]+)".*/g,
242239
filter: formatFontFilter,
243240
},
244241
{
245-
regex:
246-
/.*! Package fontspec Error:\s+\(fontspec\)\s+The font "([^"]+)" cannot be\s+\(fontspec\)\s+found;+/g,
242+
regex: /.*\(fontspec\)\s+The font "([^"]+)" cannot be.*/g,
247243
filter: formatFontFilter,
248244
},
245+
{
246+
regex: /.*Package widetext error: Install the ([^ ]+) package.*/g,
247+
filter: (match: string, _text: string) => {
248+
return `${match}.sty`;
249+
},
250+
},
249251
{ regex: /.* File `(.+eps-converted-to.pdf)'.*/g, filter: estoPdfFilter },
250252
{ regex: /.*xdvipdfmx:fatal: pdf_ref_obj.*/g, filter: estoPdfFilter },
251253

@@ -303,7 +305,7 @@ function findMissingPackages(logFileText: string): string[] {
303305
packageMatcher.regex.lastIndex = 0;
304306
});
305307

306-
// dedpulicated list of packages to attempt to install
308+
// dedulicated list of packages to attempt to install
307309
return ld.uniq(toInstall);
308310
}
309311

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* mutation-detector-proxy
3+
*
4+
* Copyright (C) 2025 Posit Software, PBC
5+
*/
6+
7+
export type DeepProxyChange = {
8+
type: "update" | "delete";
9+
path: (string | symbol)[];
10+
oldValue: unknown;
11+
newValue?: unknown;
12+
};
13+
14+
export type OnChangeCallback = (change: DeepProxyChange) => void;
15+
16+
export function mutationDetectorProxy<T extends object>(
17+
obj: T,
18+
onChange: OnChangeCallback,
19+
path: (string | symbol)[] = [],
20+
): T {
21+
return new Proxy(obj, {
22+
get(target: T, property: string | symbol): any {
23+
const value = Reflect.get(target, property);
24+
if (value && typeof value === "object") {
25+
return mutationDetectorProxy(
26+
value,
27+
onChange,
28+
[...path, property],
29+
);
30+
}
31+
return value;
32+
},
33+
34+
set(target: T, property: string | symbol, value: any): boolean {
35+
const oldValue = Reflect.get(target, property);
36+
const result = Reflect.set(target, property, value);
37+
38+
if (result) {
39+
onChange({
40+
type: "update",
41+
path: [...path, property],
42+
oldValue,
43+
newValue: value,
44+
});
45+
}
46+
47+
return result;
48+
},
49+
50+
deleteProperty(target: T, property: string | symbol): boolean {
51+
const oldValue = Reflect.get(target, property);
52+
const result = Reflect.deleteProperty(target, property);
53+
54+
if (result) {
55+
onChange({
56+
type: "delete",
57+
path: [...path, property],
58+
oldValue,
59+
});
60+
}
61+
62+
return result;
63+
},
64+
});
65+
}

src/core/jupyter/jupyter.ts

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1629,7 +1629,7 @@ async function mdFromCodeCell(
16291629
}
16301630
md.push(text.join(""));
16311631
} else {
1632-
md.push(mdOutputStream(stream));
1632+
md.push(await mdOutputStream(stream, options));
16331633
}
16341634
} else if (output.output_type === "error") {
16351635
md.push(await mdOutputError(output as JupyterOutputError, options));
@@ -1766,7 +1766,7 @@ function isMarkdown(output: JupyterOutput, options: JupyterToMarkdownOptions) {
17661766
return isDisplayDataType(output, options, displayDataIsMarkdown);
17671767
}
17681768

1769-
function mdOutputStream(output: JupyterOutputStream) {
1769+
async function mdOutputStream(output: JupyterOutputStream, options: JupyterToMarkdownOptions) {
17701770
let text: string[] = [];
17711771
if (typeof output.text === "string") {
17721772
text = [output.text];
@@ -1781,14 +1781,23 @@ function mdOutputStream(output: JupyterOutputStream) {
17811781
/<ipython-input.*?>:\d+:\s+/,
17821782
"",
17831783
);
1784-
return mdCodeOutput(
1785-
[firstLine, ...text.slice(1)].map(colors.stripColor),
1786-
);
1784+
text = [firstLine, ...text.slice(1)];
17871785
}
17881786
}
17891787

1790-
// normal default handling
1791-
return mdCodeOutput(text.map(colors.stripColor));
1788+
if (options.toHtml && text.some(hasAnsiEscapeCodes)) {
1789+
const linesHTML = await convertToHtmlSpans(text.join("\n"));
1790+
return mdMarkdownOutput(
1791+
[
1792+
"\n::: {.ansi-escaped-output}\n```{=html}\n<pre>",
1793+
linesHTML,
1794+
"</pre>\n```\n:::\n",
1795+
],
1796+
);
1797+
} else {
1798+
// normal default behavior
1799+
return mdCodeOutput(text.map(colors.stripColor));
1800+
}
17921801
}
17931802

17941803
async function mdOutputError(
@@ -1864,8 +1873,8 @@ async function mdOutputDisplayData(
18641873
// if output is invalid, warn and emit empty
18651874
const data = output.data[mimeType] as unknown;
18661875
if (!Array.isArray(data) || data.some((s) => typeof s !== "string")) {
1867-
return mdWarningOutput(`Unable to process text plain output data
1868-
which does not appear to be plain text: ${JSON.stringify(data)}`);
1876+
return await mdWarningOutput(`Unable to process text plain output data
1877+
which does not appear to be plain text: ${JSON.stringify(data)}`, options);
18691878
}
18701879
const lines = data as string[];
18711880
// pandas inexplicably outputs html tables as text/plain with an enclosing single-quote
@@ -1900,9 +1909,10 @@ which does not appear to be plain text: ${JSON.stringify(data)}`);
19001909
}
19011910

19021911
// no type match found
1903-
return mdWarningOutput(
1912+
return await mdWarningOutput(
19041913
"Unable to display output for mime type(s): " +
1905-
Object.keys(output.data).join(", "),
1914+
Object.keys(output.data).join(", "),
1915+
options,
19061916
);
19071917
}
19081918

@@ -2061,12 +2071,12 @@ function mdEnclosedOutput(begin: string, text: string[], end: string) {
20612071
return md.join("");
20622072
}
20632073

2064-
function mdWarningOutput(msg: string) {
2065-
return mdOutputStream({
2074+
async function mdWarningOutput(msg: string, options: JupyterToMarkdownOptions) {
2075+
return await mdOutputStream({
20662076
output_type: "stream",
20672077
name: "stderr",
20682078
text: [msg],
2069-
});
2079+
}, options);
20702080
}
20712081

20722082
function isWarningOutput(output: JupyterOutput) {

src/core/lib/yaml-schema/chunk-metadata.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,16 @@ const jupyterEngineSchema = defineCached(
132132
},
133133
"engine-jupyter",
134134
);
135+
const juliaEnginesSchema = defineCached(
136+
// deno-lint-ignore require-await
137+
async () => {
138+
return {
139+
schema: makeEngineSchema("julia"),
140+
errorHandlers: [],
141+
};
142+
},
143+
"engine-julia",
144+
);
135145

136146
export async function getEngineOptionsSchema(): Promise<
137147
Record<string, ConcreteSchema>
@@ -140,6 +150,7 @@ export async function getEngineOptionsSchema(): Promise<
140150
markdown: await markdownEngineSchema(),
141151
knitr: await knitrEngineSchema(),
142152
jupyter: await jupyterEngineSchema(),
153+
julia: await juliaEnginesSchema(),
143154
};
144155

145156
return obj;

0 commit comments

Comments
 (0)