Skip to content

Commit 5690e24

Browse files
committed
Display notebook title in sidebar (not filename)
1 parent 3c8af5f commit 5690e24

File tree

3 files changed

+49
-22
lines changed

3 files changed

+49
-22
lines changed

src/core/jupyter/jupyter-embed.ts

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {
4141
} from "./widgets.ts";
4242
import { globalTempContext } from "../temp.ts";
4343
import { isAbsolute } from "path/mod.ts";
44+
import { partitionMarkdown } from "../pandoc/pandoc-partition.ts";
4445

4546
export interface JupyterNotebookAddress {
4647
path: string;
@@ -58,7 +59,7 @@ export interface JupyterMarkdownOptions
5859
interface JupyterNotebookOutputCache extends ObjectWithLifetime {
5960
cache: Record<
6061
string,
61-
{ outputs: JupyterCellOutput[] }
62+
{ outputs: JupyterCellOutput[]; title?: string }
6263
>;
6364
}
6465

@@ -213,7 +214,7 @@ async function notebookMarkdown(
213214
options?: JupyterMarkdownOptions,
214215
) {
215216
// Get the cell outputs for this notebook
216-
const cellOutputs = await getCellOutputs(
217+
const notebookInfo = await getCachedNotebookInfo(
217218
nbAddress,
218219
assets,
219220
context,
@@ -223,8 +224,13 @@ async function notebookMarkdown(
223224

224225
// Wrap any injected cells with a div that includes a back link to
225226
// the notebook that originated the cells
226-
const notebookMarkdown = (cells: JupyterCellOutput[]) => {
227-
const markdown = ["", `:::{notebook="${nbAddress.path}"}`];
227+
const notebookMarkdown = (cells: JupyterCellOutput[], title?: string) => {
228+
const markdown = [
229+
"",
230+
`:::{notebook="${nbAddress.path}" ${
231+
title ? `notebook-title="${title}"` : ""
232+
}}`,
233+
];
228234
markdown.push("");
229235
markdown.push(cells.map((cell) => cell.markdown).join(""));
230236
markdown.push("");
@@ -237,7 +243,7 @@ async function notebookMarkdown(
237243
// those cells (cellIds can eiher be an explicitly set cellId, a label in the
238244
// cell metadata, or a tag on a cell that matches an id)
239245
const theCells = nbAddress.ids.map((id) => {
240-
const cell = cellForId(id, cellOutputs);
246+
const cell = cellForId(id, notebookInfo.outputs);
241247
if (cell === undefined) {
242248
throw new Error(
243249
`The cell ${id} does not exist in notebook`,
@@ -246,26 +252,26 @@ async function notebookMarkdown(
246252
return cell;
247253
}
248254
});
249-
return notebookMarkdown(theCells);
255+
return notebookMarkdown(theCells, notebookInfo.title);
250256
} else if (nbAddress.indexes) {
251257
// Filter and sort based upon cell indexes
252258
const theCells = nbAddress.indexes.map((idx) => {
253-
if (idx < 0 || idx >= cellOutputs.length) {
259+
if (idx < 0 || idx >= notebookInfo.outputs.length) {
254260
throw new Error(
255261
`The cell index ${idx} isn't within the range of cells`,
256262
);
257263
}
258-
return cellOutputs[idx];
264+
return notebookInfo.outputs[idx];
259265
});
260-
return notebookMarkdown(theCells);
266+
return notebookMarkdown(theCells, notebookInfo.title);
261267
} else {
262268
// Return all the cell outputs as there is no addtional
263269
// specification of cells
264-
return notebookMarkdown(cellOutputs);
270+
return notebookMarkdown(notebookInfo.outputs, notebookInfo.title);
265271
}
266272
}
267273

268-
async function getCellOutputs(
274+
async function getCachedNotebookInfo(
269275
nbAddress: JupyterNotebookAddress,
270276
assets: JupyterAssets,
271277
context: RenderContext,
@@ -347,11 +353,26 @@ async function getCellOutputs(
347353
},
348354
);
349355

356+
// Compute the notebook title
357+
const title = findTitle(result.cellOutputs);
358+
350359
// Place the outputs in the cache
351-
nbCache.cache[cacheKey] = { outputs: result.cellOutputs };
360+
nbCache.cache[cacheKey] = { outputs: result.cellOutputs, title };
352361
lifetime.attach(nbCache, kNotebookCache);
353362
}
354-
return nbCache.cache[cacheKey].outputs;
363+
return nbCache.cache[cacheKey];
364+
}
365+
366+
function findTitle(cells: JupyterCellOutput[]) {
367+
for (const cell of cells) {
368+
const partitioned = partitionMarkdown(cell.markdown);
369+
if (partitioned.yaml?.title) {
370+
return partitioned.yaml.title as string;
371+
} else if (partitioned.headingText) {
372+
return partitioned.headingText;
373+
}
374+
}
375+
return undefined;
355376
}
356377

357378
function notebookCacheKey(

src/format/html/format-html-bootstrap.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -505,12 +505,15 @@ function processNotebookEmbeds(
505505
) {
506506
const notebookDivNodes = doc.querySelectorAll("[data-notebook]");
507507
if (notebookDivNodes.length > 0) {
508-
const nbPaths: string[] = [];
508+
const nbPaths: { path: string; title: string | null }[] = [];
509509
notebookDivNodes.forEach((nbDivNode) => {
510510
const nbDivEl = nbDivNode as Element;
511511
const nbPath = nbDivEl.getAttribute("data-notebook");
512512
if (nbPath) {
513-
nbPaths.push(nbPath);
513+
nbPaths.push({
514+
path: nbPath,
515+
title: nbDivEl.getAttribute("data-notebook-title"),
516+
});
514517
}
515518
});
516519

@@ -525,25 +528,28 @@ function processNotebookEmbeds(
525528

526529
const formatList = doc.createElement("ul");
527530
containerEl.appendChild(formatList);
528-
ld.uniq(nbPaths).forEach((nbPath) => {
531+
ld.uniqBy(nbPaths, (nbPath: { path: string; title?: string }) => {
532+
return nbPath.path;
533+
}).forEach((nbPath) => {
534+
const filename = basename(nbPath.path);
529535
const li = doc.createElement("li");
530536

531537
const link = doc.createElement("a");
532-
link.setAttribute("href", nbPath);
533-
link.setAttribute("download", basename(nbPath));
538+
link.setAttribute("href", nbPath.path);
539+
link.setAttribute("download", filename);
534540

535541
const icon = doc.createElement("i");
536542
icon.classList.add("bi");
537543
icon.classList.add(`bi-journal-arrow-down`);
538544
link.appendChild(icon);
539545
link.appendChild(
540-
doc.createTextNode(`${basename(nbPath)}`),
546+
doc.createTextNode(`${nbPath.title || filename}`),
541547
);
542548

543549
li.appendChild(link);
544550
formatList.appendChild(li);
545551

546-
resources.push(nbPath);
552+
resources.push(nbPath.path);
547553
});
548554
let dlLinkTarget = doc.querySelector(`nav[role="doc-toc"]`);
549555
if (dlLinkTarget === null) {

src/resources/language/_language.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
toc-title-document: "Table of contents"
22
toc-title-website: "On this page"
33

4-
related-formats-title: "Alternate Formats"
5-
related-notebooks-title: "Source Notebooks"
4+
related-formats-title: "Other Formats"
5+
related-notebooks-title: "Notebooks"
66

77
section-title-abstract: "Abstract"
88
section-title-appendices: "Appendices"

0 commit comments

Comments
 (0)