Skip to content

Commit dd7fd94

Browse files
committed
Merge branch 'main' into feature/cliffy-info
2 parents 4f897ac + fbc59a3 commit dd7fd94

File tree

15 files changed

+2112
-2082
lines changed

15 files changed

+2112
-2082
lines changed

CITATION.cff

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ authors:
2020
given-names: "Gordon"
2121
orcid: "https://orcid.org/0009-0005-1809-8936"
2222
title: "Quarto"
23-
version: 1.6
23+
version: 1.7
2424
doi: 10.5281/zenodo.5960048
25-
date-released: 2024-11-27
25+
date-released: 2025-04-28
2626
url: "https://github.com/quarto-dev/quarto-cli"

configuration

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export ALGOLIA_SEARCH_INSIGHTS_JS=2.0.3
6161

6262

6363
# Quarto Info Version
64-
export QUARTO_VERSION=1.7
64+
export QUARTO_VERSION=1.8
6565
export QUARTO_NAME=Quarto
6666

6767
# Folder names. These are not the same as paths (those variable names end in _PATH).

dev-docs/checklist-make-a-new-quarto-release.md

+18-17
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,21 @@
22
- [ ] create a branch `v1.x`, where x is the version being released
33
- `git checkout -b v1.4`
44
- `git push origin v1.4`
5-
- [ ] mark the current release as the stable release in the `main` branch
5+
- [ ] mark the current release as the new release in the `main` branch
66
- [ ] edit QUARTO_VERSION line in `/configuration` to be the new version (e.g. `1.5`)
77
- [ ] push the changes to the `main` branch
88
- [ ] kick off a v1.5 build in GHA: https://github.com/quarto-dev/quarto-cli/actions/workflows/create-release.yml
99
- [ ] ensure the build completes successfully
1010
- [ ] mark v1.4 release as stable
11-
- go to https://github.com/quarto-dev/quarto-cli/releases
12-
- find the latest v1.4 release and edit, (eg https://github.com/quarto-dev/quarto-cli/releases/edit/v1.4.549)
13-
- at the bottom of the page, there will be two checkboxes, "Set as pre-release" and "Set as latest release":
14-
- "Set as pre-release" should be unchecked, and
15-
- "Set as latest release" should be checked.
11+
- [ ] go to https://github.com/quarto-dev/quarto-cli/releases
12+
- [ ] find the latest v1.4 release and edit, (eg https://github.com/quarto-dev/quarto-cli/releases/edit/v1.4.549)
13+
- [ ] at the bottom of the page, there will be two checkboxes, "Set as pre-release" and "Set as latest release":
14+
- [ ] "Set as pre-release" should be unchecked, and
15+
- [ ] "Set as latest release" should be checked.
1616
- [ ] once the v1.5 build completes, edit the quarto.org website configuration on https://github.com/quarto-dev/quarto-web to reflect the new version
1717
- this means flipping the profile group configuration in `_quarto.yml` from `[rc,prelease]` to `[prerelease,rc]`
1818
- [ ] push the changes to the `main` branch
1919
- [ ] quarto-dev/quarto-web changes
20-
2120
- wait for the downloads file to be automatically updated by the GitHub Action on https://github.com/quarto-dev/quarto-web
2221
- [ ] wait for https://github.com/quarto-dev/quarto-web/actions/workflows/update-downloads.yml to run (it runs every 15 minutes, or you can manually trigger it)
2322
- This workflow run rendered the website: https://github.com/quarto-dev/quarto-web/actions/runs/12016407762
@@ -30,20 +29,22 @@
3029
- [ ] create `docs/prerelease/1.5/{_highlights, index, _pre-release-feature}.qmd` files based on the ones from the previous release
3130
- [ ] change `docs/prerelease/_highlights.qmd` so its include points to the new version-specific `_highlights.qmd` file (here, 1.5)
3231
- [ ] change `docs/prerelease/_highlights-release.qmd` so its include points to the new version-specific `_highlights.qmd` file (here, 1.4)
33-
- [ ] add the stable version to the older downloads list, like [this example](https://github.com/quarto-dev/quarto-web/commit/85ef62ec5036026d62d57f9cfb190d8b923b2d43)
34-
- [ ] run `quarto run tools/release-notes.R` to generate the release notes
32+
- [ ] add the stable version to the older downloads list by editing /docs/download/_download-older.yml
33+
- [ ] run `quarto run tools/release-notes.R` to generate the release notes
3534
- [ ] push the changes to `prerelease` branch, ensure they build correctly
36-
- [ ] Merge the `prerelease` branch into `main`
35+
- [ ] Merge the `prerelease` branch into `main`, push to `main`
3736
- [ ] ensure the build completes successfully
38-
- [ ] Merge `main` into `prerelease`
37+
- [ ] Merge `main` into `prerelease`, push to `prerelease`
3938
- [ ] ensure the build completes successfully
40-
- [ ] Create new tag on `main` (here, `v1.5`)
41-
- [ ] `git tag -a v1.5 -m "v1.5"`
42-
- [ ] `git push origin v1.5`
39+
- [ ] Create new tag on `main` with stable release version number (here, `v1.4`) to mark when the new main site version went live
40+
- [ ] `git tag -a v1.4 -m "v1.4"`
41+
- [ ] `git push origin v1.4`
4342
- [ ] Update `prerelease` version number (here, `v1.5`)
4443
- [ ] edit `_quarto-prerelease-docs.yml` to point to the new version
4544
- [ ] publish the release blog post that should exist in https://github.com/quarto-dev/quarto-web/tree/main/docs/blog/posts
46-
by removing the `draft: true` line in the metadata and changing the date to match the release date. Do this on a branch off of `main` to trigger our PR automation to make the corresponding change to `prerelease`.
45+
- [ ] Create a branch off of `main` (to trigger our PR automation to make the corresponding change to `prerelease`).
46+
- [ ] Removing the `draft: true` line in the metadata
47+
- [ ] Change the date to match the release date.
4748

4849
- [ ] Update https://github.com/quarto-dev/quarto-cli/blob/main/CITATION.cff
4950
- [ ] Packaging and package managers, etc
@@ -55,8 +56,8 @@
5556
- Check the "Whether to publish or not the package on chocolatey" checkbox
5657
- Wait for @cderv to receive email confirmation, no action needed
5758
- [ ] pypi
58-
- Goto the [quarto-cli-pypi repo](https://github.com/quarto-dev/quarto-cli-pypi)
59-
- Update `version.txt` to be the version you'd like to publish and commit
59+
- Go to the [quarto-cli-pypi repo](https://github.com/quarto-dev/quarto-cli-pypi)
60+
- [ ] Update `version.txt` to be the version you'd like to publish and commit
6061
- Go to actions
6162
- Select 'Publish Quarto PyPi'
6263
- [ ] Click "Run Workflow"

news/changelog-1.8.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
All changes included in 1.8:
2+
3+
## Formats
4+
5+
### `revealjs`
6+
7+
- ([#12598](https://github.com/quarto-dev/quarto-cli/pull/12598)): Ensure `.fragment` on an image with caption applies to whole figure.
8+
9+
## Projects
10+
11+
### `website`
12+
13+
- ([#12551](https://github.com/quarto-dev/quarto-cli/pull/12551)): Improve warning issued when `aliases` would overwrite an existing document.
14+
- ([#12616](https://github.com/quarto-dev/quarto-cli/issues/12616)): find SVG images in image discovery for listings.

src/core/author.ts

+74-81
Original file line numberDiff line numberDiff line change
@@ -29,99 +29,92 @@ const kOrcid = "orcid";
2929
const kUrl = "url";
3030

3131
export function cslNameToString(cslName: string | CSLName) {
32-
if (typeof cslName === "string") {
33-
return cslName;
34-
} else {
35-
if (cslName.literal) {
36-
return cslName.literal;
37-
} else {
38-
const parts: string[] = [];
39-
40-
if (cslName.given) {
41-
parts.push(cslName.given);
42-
}
32+
if (typeof cslName === "string") return cslName;
33+
if (cslName.literal) return cslName.literal;
34+
const parts: string[] = [];
4335

44-
if (cslName["dropping-particle"]) {
45-
parts.push(cslName["dropping-particle"]);
46-
}
47-
if (cslName["non-dropping-particle"]) {
48-
parts.push(cslName["non-dropping-particle"]);
49-
}
50-
if (cslName.family) {
51-
parts.push(cslName.family);
52-
}
36+
if (cslName.given) {
37+
parts.push(cslName.given);
38+
}
5339

54-
return parts.join(" ");
55-
}
40+
if (cslName["dropping-particle"]) {
41+
parts.push(cslName["dropping-particle"]);
42+
}
43+
if (cslName["non-dropping-particle"]) {
44+
parts.push(cslName["non-dropping-particle"]);
5645
}
46+
if (cslName.family) {
47+
parts.push(cslName.family);
48+
}
49+
50+
return parts.join(" ");
5751
}
5852

5953
export function parseAuthor(authorRaw: unknown, strict?: boolean) {
60-
if (authorRaw) {
61-
const parsed: Author[] = [];
62-
const authors = Array.isArray(authorRaw) ? authorRaw : [authorRaw];
63-
let unrecognized = 0;
64-
authors.forEach((author) => {
65-
if (typeof author === "string") {
66-
// Its a string, so make it a name
67-
parsed.push({
68-
name: author,
69-
});
70-
} else if (typeof author === "object") {
71-
// Parse the author object
72-
// Currently this only supports simple 'Distill Style'
73-
// authors and affiliations
74-
const name = author[kName];
75-
if (name) {
76-
const auth: Author = {
77-
name,
78-
};
79-
const affilation = author[kAffiliation];
80-
if (affilation) {
81-
auth.affilliation = { name: affilation };
82-
if (author[kAfilliationUrl]) {
83-
auth.affilliation.url = author[kAfilliationUrl];
84-
}
85-
}
86-
87-
const orcid = author[kOrcid];
88-
if (orcid) {
89-
auth.orcid = orcid;
54+
if (!authorRaw) {
55+
return undefined;
56+
}
57+
const parsed: Author[] = [];
58+
const authors = Array.isArray(authorRaw) ? authorRaw : [authorRaw];
59+
let unrecognized = false;
60+
authors.forEach((author) => {
61+
if (typeof author === "string") {
62+
// Its a string, so make it a name
63+
parsed.push({
64+
name: author,
65+
});
66+
} else if (typeof author === "object") {
67+
// Parse the author object
68+
// Currently this only supports simple 'Distill Style'
69+
// authors and affiliations
70+
const name = author[kName];
71+
if (name) {
72+
const auth: Author = {
73+
name,
74+
};
75+
const affilation = author[kAffiliation];
76+
if (affilation) {
77+
auth.affilliation = { name: affilation };
78+
if (author[kAfilliationUrl]) {
79+
auth.affilliation.url = author[kAfilliationUrl];
9080
}
81+
}
9182

92-
const url = author[kUrl];
93-
if (url) {
94-
auth.url = url;
95-
}
83+
const orcid = author[kOrcid];
84+
if (orcid) {
85+
auth.orcid = orcid;
86+
}
9687

97-
parsed.push(auth);
98-
} else if (author[kFamily]) {
99-
const given = author[kGiven];
100-
const family = author[kFamily];
101-
const dropping = author[kDropping];
102-
const nonDropping = author[kNonDropping];
103-
parsed.push({
104-
name: {
105-
[kGiven]: given,
106-
[kFamily]: family,
107-
[kDropping]: dropping,
108-
[kNonDropping]: nonDropping,
109-
},
110-
});
111-
} else {
112-
unrecognized = unrecognized + 1;
88+
const url = author[kUrl];
89+
if (url) {
90+
auth.url = url;
11391
}
114-
}
115-
});
11692

117-
// If we didn't know how to parse this author
118-
// just stand down - we just don't recognize this.
119-
if (strict && unrecognized > 0) {
120-
return undefined;
121-
} else {
122-
return parsed;
93+
parsed.push(auth);
94+
} else if (author[kFamily]) {
95+
const given = author[kGiven];
96+
const family = author[kFamily];
97+
const dropping = author[kDropping];
98+
const nonDropping = author[kNonDropping];
99+
parsed.push({
100+
name: {
101+
[kGiven]: given,
102+
[kFamily]: family,
103+
[kDropping]: dropping,
104+
[kNonDropping]: nonDropping,
105+
},
106+
});
107+
} else {
108+
unrecognized = true;
109+
}
123110
}
124-
} else {
111+
});
112+
113+
// If we didn't know how to parse this author
114+
// just stand down - we just don't recognize this.
115+
if (strict && unrecognized) {
125116
return undefined;
117+
} else {
118+
return parsed;
126119
}
127120
}

src/project/types/website/listing/website-listing-read.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1159,7 +1159,7 @@ async function listItemFromFile(
11591159
)
11601160
: [];
11611161

1162-
const readingContext = target?.markdown
1162+
const readingContext = target?.markdown?.markdown
11631163
? estimateReadingTimeMinutes(target.markdown.markdown)
11641164
: undefined;
11651165
let readingtime = undefined;

src/project/types/website/util/discover-meta.ts

+16-55
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
/*
22
* discover-meta.ts
33
*
4-
* Copyright (C) 2020-2022 Posit Software, PBC
4+
* Copyright (C) 2020-2025 Posit Software, PBC
55
*/
66

77
import { Document, Element } from "deno_dom/deno-dom-wasm-noinit.ts";
8-
98
import { getDecodedAttribute } from "../../../../core/html.ts";
109

1110
// Image discovery happens by either:
@@ -15,16 +14,8 @@ import { getDecodedAttribute } from "../../../../core/html.ts";
1514

1615
const kPreviewImgClass = "preview-image";
1716
const kNamedFilePattern =
18-
"(.*?(?:preview|feature|cover|thumbnail).*?(?:\\.png|\\.gif|\\.jpg|\\.jpeg|\\.webp))";
19-
const kPreviewClassPattern =
20-
`!\\[[^\\]]*\\]\\((.*?)(?:\\".*\\")?\\)\\{[^\\|]*\\.${kPreviewImgClass}[\\s\\}]+`;
21-
const kMdNamedImagePattern =
22-
`!\\[[^\\]]*\\]\\(${kNamedFilePattern}(?:\\".*\\")?\\)(?:\\{[^\\|]*\.*[\\s\\}]+)?`;
23-
17+
"(.*?(?:preview|feature|cover|thumbnail).*?(?:\\.png|\\.gif|\\.jpg|\\.jpeg|\\.webp|\\.svg))";
2418
const kNamedFileRegex = RegExp(kNamedFilePattern, "l");
25-
const kMdPreviewClassRegex = RegExp(kPreviewClassPattern, "l");
26-
const kMdNamedImageRegex = RegExp(kMdNamedImagePattern, "l");
27-
const kMarkdownImg = /!\[[^\]]*\]\((.*?)(?:\".*\")?\)(?:\{(?:[^\|]*)\})?/;
2819

2920
export function findDescription(doc: Document): string | undefined {
3021
const paras = doc.querySelectorAll(
@@ -43,20 +34,16 @@ export function findPreviewImg(
4334
doc: Document,
4435
): { src: string; alt?: string } | undefined {
4536
const imgEl = findPreviewImgEl(doc);
46-
if (imgEl) {
47-
const src = getDecodedAttribute(imgEl, "src");
48-
const alt = getDecodedAttribute(imgEl, "alt");
49-
if (src !== null) {
50-
return {
51-
src,
52-
alt: alt ?? undefined,
53-
};
54-
} else {
55-
return undefined;
56-
}
57-
} else {
58-
return undefined;
59-
}
37+
if (!imgEl) return undefined;
38+
39+
const src = getDecodedAttribute(imgEl, "src");
40+
if (src === null) return undefined;
41+
42+
const alt = getDecodedAttribute(imgEl, "alt");
43+
return {
44+
src,
45+
alt: alt ?? undefined,
46+
};
6047
}
6148

6249
export function findPreviewImgEl(
@@ -107,34 +94,8 @@ export function findPreviewImgEl(
10794
// So 200 is a good middle ground estimate.
10895
const kWpm = 200;
10996
export function estimateReadingTimeMinutes(
110-
markdown?: string,
111-
): { wordCount: number; readingTime: number } | undefined {
112-
if (markdown) {
113-
const wordCount = markdown.split(" ").length;
114-
return { wordCount, readingTime: Math.ceil(wordCount / kWpm) };
115-
}
116-
return undefined;
117-
}
118-
119-
export function findPreviewImgMd(markdown?: string): string | undefined {
120-
if (markdown) {
121-
// Look for an explictly tagged image
122-
const explicitMatch = markdown.match(kMdPreviewClassRegex);
123-
if (explicitMatch) {
124-
return explicitMatch[1];
125-
}
126-
127-
// Look for an image with a 'magic' name
128-
const fileNameMatch = markdown.match(kMdNamedImageRegex);
129-
if (fileNameMatch) {
130-
return fileNameMatch[1];
131-
}
132-
133-
// Otherwise select the first image
134-
const match = markdown.match(kMarkdownImg);
135-
if (match) {
136-
return match[1];
137-
}
138-
}
139-
return undefined;
97+
markdown: string,
98+
): { wordCount: number; readingTime: number } {
99+
const wordCount = markdown.split(" ").length;
100+
return { wordCount, readingTime: Math.ceil(wordCount / kWpm) };
140101
}

0 commit comments

Comments
 (0)