Skip to content

Commit 367979b

Browse files
committed
Improve Notebook Ordering
- If the user provides an explicit order in manuscript configuration, be sure to respect that - If no order is provided, sort by title
1 parent 8ba8f74 commit 367979b

File tree

5 files changed

+72
-26
lines changed

5 files changed

+72
-26
lines changed

news/changelog-1.5.md

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All changes included in 1.5:
44

55
- ([#8118](https://github.com/quarto-dev/quarto-cli/issues/8118)): Add support for `body-classes` to add classes to the document body.
66

7+
## Manuscripts
8+
9+
- ([#8277](https://github.com/quarto-dev/quarto-cli/issues/8277)): Improve notebook ordering within Manuscript projects
10+
711
## Other Fixes
812

913
- ([#8119](https://github.com/quarto-dev/quarto-cli/issues/8119)): More intelligently detect when ejs templates are modified during development, improving quality of life during preview.

src/config/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ export interface NotebookPreviewDescriptor {
343343
url?: string;
344344
title?: string;
345345
[kDownloadUrl]?: string;
346+
order?: number;
346347
}
347348

348349
export interface FormatExtras {

src/format/html/format-html-notebook-preview.ts

+20-8
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ export interface NotebookPreview {
3232
filename?: string;
3333
supporting?: string[];
3434
resources?: string[];
35+
order?: number;
3536
}
3637

3738
export interface NotebookPreviewTask {
3839
input: string;
3940
nbPath: string;
4041
title?: string;
4142
nbPreviewFile?: string;
43+
order?: number;
4244
callback?: (nbPreview: NotebookPreview) => void;
4345
}
4446

@@ -54,7 +56,7 @@ export const notebookPreviewer = (
5456

5557
const nbDescriptors: Record<string, NotebookPreviewDescriptor> = {};
5658
if (nbView) {
57-
if (typeof (nbView) !== "boolean") {
59+
if (typeof nbView !== "boolean") {
5860
asArray(nbView).forEach((view) => {
5961
const existingView = nbDescriptors[view.notebook];
6062
nbDescriptors[view.notebook] = {
@@ -74,15 +76,23 @@ export const notebookPreviewer = (
7476
input: string,
7577
nbAbsPath: string,
7678
title?: string,
79+
order?: number,
7780
callback?: (nbPreview: NotebookPreview) => void,
7881
) => {
79-
// Try to provide a title
80-
previewQueue.push({
81-
input,
82-
nbPath: nbAbsPath,
83-
title: title,
84-
callback,
85-
});
82+
if (
83+
!previewQueue.find((work) => {
84+
return work.nbPath === nbAbsPath;
85+
})
86+
) {
87+
// Try to provide a title
88+
previewQueue.push({
89+
input,
90+
nbPath: nbAbsPath,
91+
title: title,
92+
callback,
93+
order,
94+
});
95+
}
8696
};
8797

8898
const renderPreviews = async (output?: string, quiet?: boolean) => {
@@ -245,6 +255,7 @@ export const notebookPreviewer = (
245255
href: relative(inputDir, renderedNotebook[kHtmlPreview].hrefPath),
246256
supporting,
247257
resources,
258+
order: work.order,
248259
};
249260
rendered[work.nbPath] = nbPreview;
250261
if (work.callback) {
@@ -255,6 +266,7 @@ export const notebookPreviewer = (
255266
href: pathWithForwardSlashes(join(nbDir, filename)),
256267
title: title || filename,
257268
filename,
269+
order: work.order,
258270
};
259271
rendered[work.nbPath] = nbPreview;
260272
if (work.callback) {

src/format/html/format-html-notebook.ts

+17-3
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ export async function emplaceNotebookPreviews(
153153
input,
154154
input,
155155
undefined, // title
156+
undefined, // order
156157
(nbPreview) => {
157158
// If this is a cell _in_ a source notebook, it will not be parented
158159
// by an embed cell
@@ -175,7 +176,7 @@ export async function emplaceNotebookPreviews(
175176
}
176177

177178
// For any notebooks explicitly provided, ensure they are rendered
178-
if (typeof (notebookView) !== "boolean") {
179+
if (typeof notebookView !== "boolean") {
179180
const nbs = Array.isArray(notebookView) ? notebookView : [notebookView];
180181
for (const nb of nbs) {
181182
// Filter out the root article notebook, since that was resolved
@@ -184,7 +185,7 @@ export async function emplaceNotebookPreviews(
184185
const nbAbsPath = isAbsolute(nb.notebook)
185186
? nb.notebook
186187
: join(dirname(input), nb.notebook);
187-
previewer.enQueuePreview(input, nbAbsPath, nb.title);
188+
previewer.enQueuePreview(input, nbAbsPath, nb.title, nb.order);
188189
}
189190
}
190191
}
@@ -208,6 +209,7 @@ export async function emplaceNotebookPreviews(
208209
input,
209210
nbAbsPath(input, notebookPath),
210211
title === null ? undefined : title,
212+
undefined, // order
211213
(nbPreview) => {
212214
// Add a decoration to this div node
213215
if (inline) {
@@ -221,8 +223,20 @@ export async function emplaceNotebookPreviews(
221223
// Render the notebook previews
222224
const previews = await previewer.renderPreviews(output, quiet);
223225

226+
// Get the preview notebooks in the correct order
227+
const previewNotebooks = Object.values(previews).sort((a, b) => {
228+
if (a.order !== undefined && b.order !== undefined) {
229+
return a.order - b.order;
230+
} else if (a.order !== undefined && b.order === undefined) {
231+
return -1;
232+
} else if (a.order === undefined && b.order !== undefined) {
233+
return 1;
234+
} else {
235+
return a.title.localeCompare(b.title);
236+
}
237+
});
238+
224239
// Emit global links to the notebooks
225-
const previewNotebooks = Object.values(previews);
226240
if (global && previewNotebooks.length > 0) {
227241
const containerEl = doc.createElement("div");
228242
containerEl.classList.add("quarto-alternate-notebooks");

src/project/types/manuscript/manuscript.ts

+30-15
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,12 @@ export const manuscriptProjectType: ProjectType = {
157157
const article = computeProjectArticleFile(projectDir, manuscriptConfig);
158158

159159
// Go through project inputs and use any of these as notebooks
160-
const notebooks: Record<string, NotebookPreviewDescriptor> = {};
160+
const notebooks: NotebookPreviewDescriptor[] = [];
161161

162162
const explicitNotebooks = manuscriptConfig[kNotebooks];
163163
if (explicitNotebooks) {
164-
resolveNotebookDescriptors(explicitNotebooks).forEach((nb) => {
165-
notebooks[nb.notebook] = nb;
164+
resolveNotebookDescriptors(explicitNotebooks, true).forEach((nb) => {
165+
notebooks.push(nb);
166166
});
167167
} else {
168168
const inputNotebooks = inputs.files.map((input) => {
@@ -191,7 +191,7 @@ export const manuscriptProjectType: ProjectType = {
191191

192192
if (inputNotebooks) {
193193
resolveNotebookDescriptors(inputNotebooks).forEach((nb) => {
194-
notebooks[nb.notebook] = nb;
194+
notebooks.push(nb);
195195
});
196196
}
197197
}
@@ -219,10 +219,17 @@ export const manuscriptProjectType: ProjectType = {
219219
// If there are computations in the main article, the add
220220
// it as a notebook to be rendered with computations intact
221221
if (await hasComputations(join(projectDir, article))) {
222-
notebooks[article] = {
223-
notebook: article,
224-
title: language[kArticleNotebookLabel],
225-
};
222+
if (
223+
!notebooks.find((nb) => {
224+
return nb.notebook === article;
225+
})
226+
) {
227+
notebooks.unshift({
228+
notebook: article,
229+
title: language[kArticleNotebookLabel],
230+
order: 9999,
231+
});
232+
}
226233
jatsNotebooks.unshift({
227234
input: join(projectDir, article),
228235
token: `nb-article`,
@@ -232,6 +239,7 @@ export const manuscriptProjectType: ProjectType = {
232239

233240
// Determine the notebooks that are being declared explicitly in
234241
// in the manuscript configuration
242+
/*
235243
if (manuscriptConfig.notebooks !== undefined) {
236244
const specifiedNotebooks = Array.isArray(manuscriptConfig.notebooks)
237245
? manuscriptConfig.notebooks
@@ -240,6 +248,7 @@ export const manuscriptProjectType: ProjectType = {
240248
notebooks[nb.notebook] = nb;
241249
});
242250
}
251+
*/
243252

244253
// Note JATS subarticles for the JATS format
245254
config[kQuartoInternal] = {
@@ -263,7 +272,7 @@ export const manuscriptProjectType: ProjectType = {
263272
const resolvedManuscriptOptions: ResolvedManuscriptConfig = {
264273
...manuscriptConfig,
265274
article,
266-
notebooks: Object.values(notebooks),
275+
notebooks: notebooks,
267276
mecaFile: mecaFileOutput,
268277
[kEnvironmentFiles]: environmentFiles,
269278
};
@@ -505,8 +514,8 @@ export const manuscriptProjectType: ProjectType = {
505514
// If the user isn't explicitly providing a notebook list
506515
// then automatically create notebooks for the other items in
507516
// the project
508-
const outputNbs: Record<string, NotebookPreviewDescriptor> = {};
509517
const notebooks = manuscriptConfig.notebooks || [];
518+
const orderedNbs: NotebookPreviewDescriptor[] = [];
510519
for (const notebook of notebooks) {
511520
// Use the input to create a title for the notebook
512521
// if needed
@@ -521,12 +530,12 @@ export const manuscriptProjectType: ProjectType = {
521530
}
522531
};
523532

524-
outputNbs[notebook.notebook] = {
533+
orderedNbs.push({
525534
...notebook,
526535
title: notebook.title || await createTitle(),
527-
};
536+
});
528537
}
529-
extras[kNotebooks] = Object.values(outputNbs);
538+
extras[kNotebooks] = orderedNbs;
530539
} else if (isArticle && isLatexOutput(format.pandoc)) {
531540
if (isLatexOutput(format.pandoc)) {
532541
// By default, keep tex and clean things up ourselves
@@ -687,19 +696,25 @@ const hasComputations = async (file: string) => {
687696

688697
const resolveNotebookDescriptor = (
689698
nb: string | NotebookPreviewDescriptor,
699+
order?: number,
690700
): NotebookPreviewDescriptor => {
691701
if (typeof nb === "string") {
692-
nb = { notebook: nb };
702+
nb = { notebook: nb, order };
693703
}
694704
return nb;
695705
};
696706

697707
const resolveNotebookDescriptors = (
698708
nbs: Array<string | NotebookPreviewDescriptor>,
709+
orderNotebooks = false,
699710
) => {
700711
const resolvedNbs: NotebookPreviewDescriptor[] = [];
712+
let order = 0;
701713
for (const nb of nbs) {
702-
resolvedNbs.push(resolveNotebookDescriptor(nb));
714+
resolvedNbs.push(
715+
resolveNotebookDescriptor(nb, orderNotebooks ? order : undefined),
716+
);
717+
order++;
703718
}
704719
return resolvedNbs;
705720
};

0 commit comments

Comments
 (0)