Printable list collection (#7812)

This commit is contained in:
Elian Doran 2025-11-20 21:52:55 +02:00 committed by GitHub
commit 26a009b397
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 259 additions and 73 deletions

View File

@ -467,28 +467,30 @@ function getReferenceLinkTitleSync(href: string) {
}
}
// TODO: Check why the event is not supported.
//@ts-ignore
$(document).on("click", "a", goToLink);
// TODO: Check why the event is not supported.
//@ts-ignore
$(document).on("auxclick", "a", goToLink); // to handle the middle button
// TODO: Check why the event is not supported.
//@ts-ignore
$(document).on("contextmenu", "a", linkContextMenu);
// TODO: Check why the event is not supported.
//@ts-ignore
$(document).on("dblclick", "a", goToLink);
if (glob.device !== "print") {
// TODO: Check why the event is not supported.
//@ts-ignore
$(document).on("click", "a", goToLink);
// TODO: Check why the event is not supported.
//@ts-ignore
$(document).on("auxclick", "a", goToLink); // to handle the middle button
// TODO: Check why the event is not supported.
//@ts-ignore
$(document).on("contextmenu", "a", linkContextMenu);
// TODO: Check why the event is not supported.
//@ts-ignore
$(document).on("dblclick", "a", goToLink);
$(document).on("mousedown", "a", (e) => {
if (e.which === 2) {
// prevent paste on middle click
// https://github.com/zadam/trilium/issues/2995
// https://developer.mozilla.org/en-US/docs/Web/API/Element/auxclick_event#preventing_default_actions
e.preventDefault();
return false;
}
});
$(document).on("mousedown", "a", (e) => {
if (e.which === 2) {
// prevent paste on middle click
// https://github.com/zadam/trilium/issues/2995
// https://developer.mozilla.org/en-US/docs/Web/API/Element/auxclick_event#preventing_default_actions
e.preventDefault();
return false;
}
});
}
export default {
getNotePathFromUrl,

View File

@ -13,6 +13,7 @@ import { subscribeToMessages, unsubscribeToMessage as unsubscribeFromMessage } f
import { WebSocketMessage } from "@triliumnext/commons";
import froca from "../../services/froca";
import PresentationView from "./presentation";
import { ListPrintView } from "./legacy/ListPrintView";
interface NoteListProps {
note: FNote | null | undefined;
@ -103,7 +104,11 @@ export function CustomNoteList({ note, viewType, isEnabled: shouldEnable, notePa
function getComponentByViewType(viewType: ViewTypeOptions, props: ViewModeProps<any>) {
switch (viewType) {
case "list":
return <ListView {...props} />;
if (props.media !== "print") {
return <ListView {...props} />;
} else {
return <ListPrintView {...props} />;
}
case "grid":
return <GridView {...props} />;
case "geoMap":

View File

@ -1,4 +1,4 @@
import { useEffect, useMemo, useRef, useState } from "preact/hooks";
import { useEffect, useRef, useState } from "preact/hooks";
import FNote from "../../../entities/fnote";
import Icon from "../../react/Icon";
import { ViewModeProps } from "../interface";
@ -11,6 +11,7 @@ import tree from "../../../services/tree";
import link from "../../../services/link";
import { t } from "../../../services/i18n";
import attribute_renderer from "../../../services/attribute_renderer";
import { filterChildNotes, useFilteredNoteIds } from "./utils";
export function ListView({ note, noteIds: unfilteredNoteIds, highlightedTokens }: ViewModeProps<{}>) {
const [ isExpanded ] = useNoteLabelBoolean(note, "expanded");
@ -160,30 +161,15 @@ function NoteContent({ note, trim, noChildrenList, highlightedTokens }: { note:
}
function NoteChildren({ note, parentNote, highlightedTokens }: { note: FNote, parentNote: FNote, highlightedTokens: string[] | null | undefined }) {
const imageLinks = note.getRelations("imageLink");
const [ childNotes, setChildNotes ] = useState<FNote[]>();
useEffect(() => {
note.getChildNotes().then(childNotes => {
const filteredChildNotes = childNotes.filter((childNote) => !imageLinks.find((rel) => rel.value === childNote.noteId));
setChildNotes(filteredChildNotes);
});
filterChildNotes(note).then(setChildNotes);
}, [ note ]);
return childNotes?.map(childNote => <ListNoteCard note={childNote} parentNote={parentNote} highlightedTokens={highlightedTokens} />)
}
/**
* Filters the note IDs for the legacy view to filter out subnotes that are already included in the note content such as images, included notes.
*/
function useFilteredNoteIds(note: FNote, noteIds: string[]) {
return useMemo(() => {
const includedLinks = note ? note.getRelations().filter((rel) => rel.name === "imageLink" || rel.name === "includeNoteLink") : [];
const includedNoteIds = new Set(includedLinks.map((rel) => rel.value));
return noteIds.filter((noteId) => !includedNoteIds.has(noteId) && noteId !== "_hidden");
}, noteIds);
}
function getNotePath(parentNote: FNote, childNote: FNote) {
if (parentNote.type === "search") {
// for search note parent, we want to display a non-search path

View File

@ -0,0 +1,110 @@
import { useEffect, useLayoutEffect, useState } from "preact/hooks";
import froca from "../../../services/froca";
import type FNote from "../../../entities/fnote";
import content_renderer from "../../../services/content_renderer";
import type { ViewModeProps } from "../interface";
import { filterChildNotes, useFilteredNoteIds } from "./utils";
interface NotesWithContent {
note: FNote;
contentEl: HTMLElement;
}
export function ListPrintView({ note, noteIds: unfilteredNoteIds, onReady }: ViewModeProps<{}>) {
const noteIds = useFilteredNoteIds(note, unfilteredNoteIds);
const [ notesWithContent, setNotesWithContent ] = useState<NotesWithContent[]>();
useLayoutEffect(() => {
const noteIdsSet = new Set<string>();
froca.getNotes(noteIds).then(async (notes) => {
const notesWithContent: NotesWithContent[] = [];
async function processNote(note: FNote, depth: number) {
const content = await content_renderer.getRenderedContent(note, {
trim: false,
noChildrenList: true
});
const contentEl = content.$renderedContent[0];
insertPageTitle(contentEl, note.title);
rewriteHeadings(contentEl, depth);
noteIdsSet.add(note.noteId);
notesWithContent.push({ note, contentEl });
if (note.hasChildren()) {
const filteredChildNotes = await filterChildNotes(note);
for (const childNote of filteredChildNotes) {
await processNote(childNote, depth + 1);
}
}
}
for (const note of notes) {
await processNote(note, 1);
}
// After all notes are processed, rewrite links
for (const { contentEl } of notesWithContent) {
rewriteLinks(contentEl, noteIdsSet);
}
setNotesWithContent(notesWithContent);
});
}, [noteIds]);
useEffect(() => {
if (notesWithContent && onReady) {
onReady();
}
}, [ notesWithContent, onReady ]);
return (
<div class="note-list list-print-view">
<div class="note-list-container use-tn-links">
<h1>{note.title}</h1>
{notesWithContent?.map(({ note: childNote, contentEl }) => (
<section id={`note-${childNote.noteId}`} class="note" dangerouslySetInnerHTML={{ __html: contentEl.innerHTML }} />
))}
</div>
</div>
);
}
function insertPageTitle(contentEl: HTMLElement, title: string) {
const pageTitleEl = document.createElement("h1");
pageTitleEl.textContent = title;
contentEl.prepend(pageTitleEl);
}
function rewriteHeadings(contentEl: HTMLElement, depth: number) {
const headings = contentEl.querySelectorAll("h1, h2, h3, h4, h5, h6");
for (const headingEl of headings) {
const currentLevel = parseInt(headingEl.tagName.substring(1), 10);
const newLevel = Math.min(currentLevel + depth, 6);
const newHeadingEl = document.createElement(`h${newLevel}`);
newHeadingEl.innerHTML = headingEl.innerHTML;
headingEl.replaceWith(newHeadingEl);
}
}
function rewriteLinks(contentEl: HTMLElement, noteIdsSet: Set<string>) {
const linkEls = contentEl.querySelectorAll("a");
for (const linkEl of linkEls) {
const href = linkEl.getAttribute("href");
if (href && href.startsWith("#root/")) {
const noteId = href.split("/").at(-1);
if (noteId && noteIdsSet.has(noteId)) {
linkEl.setAttribute("href", `#note-${noteId}`);
} else {
// Link to note not in the print view, remove link but keep text
const spanEl = document.createElement("span");
spanEl.innerHTML = linkEl.innerHTML;
linkEl.replaceWith(spanEl);
}
}
}
}

View File

@ -0,0 +1,20 @@
import { useMemo } from "preact/hooks";
import FNote from "../../../entities/fnote";
/**
* Filters the note IDs for the legacy view to filter out subnotes that are already included in the note content such as images, included notes.
*/
export function useFilteredNoteIds(note: FNote, noteIds: string[]) {
return useMemo(() => {
const includedLinks = note ? note.getRelations().filter((rel) => rel.name === "imageLink" || rel.name === "includeNoteLink") : [];
const includedNoteIds = new Set(includedLinks.map((rel) => rel.value));
return noteIds.filter((noteId) => !includedNoteIds.has(noteId) && noteId !== "_hidden");
}, [ note, noteIds ]);
}
export async function filterChildNotes(note: FNote) {
const imageLinks = note.getRelations("imageLink");
const imageLinkNoteIds = new Set(imageLinks.map(rel => rel.value));
const childNotes = await note.getChildNotes();
return childNotes.filter((childNote) => !imageLinkNoteIds.has(childNote.noteId));
}

View File

@ -49,7 +49,7 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
const canBeConvertedToAttachment = note?.isEligibleForConversionToAttachment();
const isSearchable = ["text", "code", "book", "mindMap", "doc"].includes(note.type);
const isInOptionsOrHelp = note?.noteId.startsWith("_options") || note?.noteId.startsWith("_help");
const isPrintable = ["text", "code"].includes(note.type) || (note.type === "book" && note.getLabelValue("viewType") === "presentation");
const isPrintable = ["text", "code"].includes(note.type) || (note.type === "book" && ["presentation", "list"].includes(note.getLabelValue("viewType") ?? ""));
const isElectron = getIsElectron();
const isMac = getIsMac();
const hasSource = ["text", "code", "relationMap", "mermaid", "canvas", "mindMap", "aiChat"].includes(note.type);

View File

@ -4,7 +4,6 @@
<figcaption>Screenshot of the note contextual menu indicating the “Export as PDF”
option.</figcaption>
</figure>
<h2>Printing</h2>
<p>This feature allows printing of notes. It works on both the desktop client,
but also on the web.</p>
@ -60,9 +59,9 @@ class="admonition note">
orientation, size. However, there are a few&nbsp;<a class="reference-link"
href="#root/_help_zEY4DaJG4YT5">Attributes</a>&nbsp;to adjust some of the settings:</p>
<ul>
<li>To print in landscape mode instead of portrait (useful for big diagrams
<li data-list-item-id="e05b1bc3a57c550c493c8b1030c301673">To print in landscape mode instead of portrait (useful for big diagrams
or slides), add <code>#printLandscape</code>.</li>
<li>By default, the resulting PDF will be in Letter format. It is possible
<li data-list-item-id="e6d7f6bb720e1f94994aa178881885dbd">By default, the resulting PDF will be in Letter format. It is possible
to adjust it to another page size via the <code>#printPageSize</code> attribute,
with one of the following values: <code>A0</code>, <code>A1</code>, <code>A2</code>, <code>A3</code>, <code>A4</code>, <code>A5</code>, <code>A6</code>, <code>Legal</code>, <code>Letter</code>, <code>Tabloid</code>, <code>Ledger</code>.</li>
</ul>
@ -70,15 +69,26 @@ class="admonition note">
<p>These options have no effect when used with the printing feature, since
the user-defined settings are used instead.</p>
</aside>
<h2>Printing multiple notes</h2>
<p>Since v0.100.0, it is possible to print more than one note at the time
by using&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/_help_GTwFsgaA0lCt">Collections</a>:</p>
<ol>
<li data-list-item-id="e1caaf943b13fd4764f93c58ea5f4f0c4">First create a collection.</li>
<li data-list-item-id="e3593024c9c69c3d26295d1e0152c813d">Configure it to use&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/GTwFsgaA0lCt/_help_mULW0Q3VojwY">List View</a>.</li>
<li
data-list-item-id="ebeea878f04af6f1da53fc0e8a80caf2d">Print the collection note normally.</li>
</ol>
<p>The resulting collection will contain all the children of the collection,
while maintaining the hierarchy.</p>
<h2>Keyboard shortcut</h2>
<p>It's possible to trigger both printing and export as PDF from the keyboard
by going to&nbsp;<em>Keyboard shortcuts</em>&nbsp;in&nbsp;<a class="reference-link"
href="#root/_help_4TIF1oA4VQRO">Options</a>&nbsp;and assigning a key combination
for:</p>
<ul>
<li><em>Print Active Note</em>
<li class="ck-list-marker-italic" data-list-item-id="e9595278e625ee8de30a6e88fb00d48e3"><em>Print Active Note</em>
</li>
<li><em>Export Active Note as PDF</em>
<li class="ck-list-marker-italic" data-list-item-id="e981d4cf371e1ff69416a796d88e88709"><em>Export Active Note as PDF</em>
</li>
</ul>
<h2>Constraints &amp; limitations</h2>
@ -86,24 +96,28 @@ class="admonition note">
supported when printing, in which case the <em>Print</em> and <em>Export as PDF</em> options
will be disabled.</p>
<ul>
<li>For&nbsp;<a class="reference-link" href="#root/_help_6f9hih2hXXZk">Code</a>&nbsp;notes:
<li data-list-item-id="e82f01875cc03dcdab5328121654d815c">For&nbsp;<a class="reference-link" href="#root/_help_6f9hih2hXXZk">Code</a>&nbsp;notes:
<ul>
<li>Line numbers are not printed.</li>
<li>Syntax highlighting is enabled, however a default theme (Visual Studio)
<li data-list-item-id="eea76e6bf545a3b54270ff86a74ca0d8d">Line numbers are not printed.</li>
<li data-list-item-id="edea65d8d3dedd354431e1e3a5dcd2e08">Syntax highlighting is enabled, however a default theme (Visual Studio)
is enforced.</li>
</ul>
</li>
<li>For&nbsp;<a class="reference-link" href="#root/_help_GTwFsgaA0lCt">Collections</a>:
<li data-list-item-id="ec32cca86e4b0e2f75a2f1a06d2219e0b">For&nbsp;<a class="reference-link" href="#root/_help_GTwFsgaA0lCt">Collections</a>:
<ul>
<li>Only&nbsp;<a class="reference-link" href="#root/_help_zP3PMqaG71Ct">Presentation</a>&nbsp;is
currently supported.</li>
<li>We plan to add support for all the collection types at some point.</li>
<li data-list-item-id="e0e1fc82e1141d3f4a609699e228ccc73"><a class="reference-link" href="#root/pOsGYCXsbNQG/GTwFsgaA0lCt/_help_mULW0Q3VojwY">List View</a>&nbsp;is
supported, allowing to print multiple notes at once while preserving hierarchy
(similar to a book).</li>
<li data-list-item-id="ee114b8468eaf24bce451f5ec4bda3da4"><a class="reference-link" href="#root/_help_zP3PMqaG71Ct">Presentation</a>&nbsp;is
also supported, where each slide/subnote is displayed.</li>
<li data-list-item-id="e4efe886c3ca1d19a49196340d9e1f6c8">The rest of the collections are not supported, but we plan to add support
for all the collection types at some point.</li>
</ul>
</li>
<li>Using&nbsp;<a class="reference-link" href="#root/_help_AlhDUqhENtH7">Custom app-wide CSS</a>&nbsp;for
<li data-list-item-id="ee721d0145486818bd914a26594699cbd">Using&nbsp;<a class="reference-link" href="#root/_help_AlhDUqhENtH7">Custom app-wide CSS</a>&nbsp;for
printing is not longer supported, due to a more stable but isolated mechanism.
<ul>
<li>We plan to introduce a new mechanism specifically for a print CSS.</li>
<li data-list-item-id="e2e1228d8d62cbdc8e96a7cbc9655c2ca">We plan to introduce a new mechanism specifically for a print CSS.</li>
</ul>
</li>
</ul>
@ -114,10 +128,10 @@ class="admonition note">
printing.</p>
<p>To do so:</p>
<ul>
<li>Create a CSS <a href="#root/_help_6f9hih2hXXZk">code note</a>.</li>
<li>On the note being printed, apply the <code>~printCss</code> relation to
<li data-list-item-id="ea90c233190428f0aacfcca4abe2f6b18">Create a CSS <a href="#root/_help_6f9hih2hXXZk">code note</a>.</li>
<li data-list-item-id="ec0756dfa1ce83087dd2c9bcc289d234b">On the note being printed, apply the <code>~printCss</code> relation to
point to the newly created CSS code note.</li>
<li>To apply the CSS to multiple notes, consider using <a href="#root/_help_bwZpz2ajCEwO">inheritable attributes</a> or&nbsp;
<li data-list-item-id="ed05b40a29e6b442327bf439286096ac6">To apply the CSS to multiple notes, consider using <a href="#root/_help_bwZpz2ajCEwO">inheritable attributes</a> or&nbsp;
<a
class="reference-link" href="#root/_help_KC1HB96bqqHX">Templates</a>.</li>
</ul>
@ -128,12 +142,13 @@ class="admonition note">
}</code></pre>
<p>To remark:</p>
<ul>
<li>Multiple CSS notes can be add by using multiple <code>~printCss</code> relations.</li>
<li>If the note pointing to the <code>printCss</code> doesn't have the right
<li data-list-item-id="ec7fa7fb43c85ba65185b42a9ed590da7">Multiple CSS notes can be add by using multiple <code>~printCss</code> relations.</li>
<li
data-list-item-id="e1db64e345bbaf53151b84a69ff91376f">If the note pointing to the <code>printCss</code> doesn't have the right
note type or mime type, it will be ignored.</li>
<li>If migrating from a previous version where&nbsp;<a class="reference-link"
href="#root/_help_AlhDUqhENtH7">Custom app-wide CSS</a>, there's no need for <code>@media print {</code> since
the style-sheet is used only for printing.</li>
<li data-list-item-id="e8b2d24c4a6781c5516d0551f51b3947b">If migrating from a previous version where&nbsp;<a class="reference-link"
href="#root/_help_AlhDUqhENtH7">Custom app-wide CSS</a>, there's no need for <code>@media print {</code> since
the style-sheet is used only for printing.</li>
</ul>
<h2>Under the hood</h2>
<p>Both printing and exporting as PDF use the same mechanism: a note is rendered

View File

@ -12,9 +12,22 @@
as a single continuous document.</p>
<h2>Interaction</h2>
<ul>
<li>Each note can be expanded or collapsed by clicking on the arrow to the
<li data-list-item-id="ee85c9dce1f91b700d8f13bdc9500bc62">Each note can be expanded or collapsed by clicking on the arrow to the
left of the title.</li>
<li>In the&nbsp;<a class="reference-link" href="#root/_help_BlN9DFI679QC">Ribbon</a>,
<li data-list-item-id="e84faa71c2b0bf22a09490b35134f2687">In the&nbsp;<a class="reference-link" href="#root/_help_BlN9DFI679QC">Ribbon</a>,
in the <em>Collection</em> tab there are options to expand and to collapse
all notes easily.</li>
</ul>
<h2>Printing and exporting to PDF</h2>
<p>Since v0.100.0, list collections can be <a href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/BFs8mudNFgCS/_help_NRnIZmSMc5sj">printed or exported to PDF</a>.</p>
<p>A printed list collection will print all the notes in the collection,
in the right order and preserving the full hierarchy.</p>
<p>If exported to PDF within the desktop application, there is additional
functionality:</p>
<ul>
<li data-list-item-id="ec4b9a29dd7f601d1415b3ca9fa414fde">The table of contents of the PDF will reflect the structure of the notes.</li>
<li
data-list-item-id="ef5fa5e9c68e7cbdf9a5e468b406e298a">Reference and inline links to other notes within the same hierarchy will
be functional (will jump to the corresponding page). If a link refers to
a note that is not in the printed hierarchy, it will be unlinked.</li>
</ul>

View File

@ -1,13 +1,12 @@
<ul>
<li data-list-item-id="ef125f092e6b27dc2c8486d195be8be39"><code>doRender</code> must not be overridden, instead <code>doRenderBody()</code> has
<li><code>doRender</code> must not be overridden, instead <code>doRenderBody()</code> has
to be overridden.
<ul>
<li data-list-item-id="e632e4415d4439f862124dc2e024093db"><code>doRenderBody</code> can optionally be <code>async</code>.</li>
<li><code>doRenderBody</code> can optionally be <code>async</code>.</li>
</ul>
</li>
<li data-list-item-id="eef79d03bdc04d4869f6ee69f9086931f"><code>parentWidget()</code> must be set to <code>“rightPane”</code>.</li>
<li
data-list-item-id="e1bbd084dfcfdea1a8061159b8dcffd44"><code>widgetTitle()</code> getter can optionally be overriden, otherwise
<li><code>parentWidget()</code> must be set to <code>“rightPane”</code>.</li>
<li><code>widgetTitle()</code> getter can optionally be overriden, otherwise
the widget will be displayed as “Untitled widget”.</li>
</ul><pre><code class="language-text-x-trilium-auto">const template = `&lt;div&gt;Hi&lt;/div&gt;`;

View File

@ -1,5 +1,5 @@
# Documentation
There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/veLUtimyaTkr/Documentation_image.png" width="205" height="162">
There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/CJFtZbAX4Otj/Documentation_image.png" width="205" height="162">
* The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing <kbd>F1</kbd>.
* The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers.

View File

@ -4135,6 +4135,13 @@
"value": "printing-and-pdf-export",
"isInheritable": false,
"position": 110
},
{
"type": "relation",
"name": "internalLink",
"value": "mULW0Q3VojwY",
"isInheritable": false,
"position": 130
}
],
"format": "markdown",
@ -10478,6 +10485,13 @@
"value": "list",
"isInheritable": false,
"position": 30
},
{
"type": "relation",
"name": "internalLink",
"value": "NRnIZmSMc5sj",
"isInheritable": false,
"position": 40
}
],
"format": "markdown",

View File

@ -49,6 +49,16 @@ When exporting to PDF, there are no customizable settings such as page orientati
> [!NOTE]
> These options have no effect when used with the printing feature, since the user-defined settings are used instead.
## Printing multiple notes
Since v0.100.0, it is possible to print more than one note at the time by using <a class="reference-link" href="../../Collections.md">Collections</a>:
1. First create a collection.
2. Configure it to use <a class="reference-link" href="../../Collections/List%20View.md">List View</a>.
3. Print the collection note normally.
The resulting collection will contain all the children of the collection, while maintaining the hierarchy.
## Keyboard shortcut
It's possible to trigger both printing and export as PDF from the keyboard by going to _Keyboard shortcuts_ in <a class="reference-link" href="../UI%20Elements/Options.md">Options</a> and assigning a key combination for:
@ -64,8 +74,9 @@ Not all <a class="reference-link" href="../../Note%20Types.md">Note Types</a> 
* Line numbers are not printed.
* Syntax highlighting is enabled, however a default theme (Visual Studio) is enforced.
* For <a class="reference-link" href="../../Collections.md">Collections</a>:
* Only <a class="reference-link" href="../../Collections/Presentation.md">Presentation</a> is currently supported.
* We plan to add support for all the collection types at some point.
* <a class="reference-link" href="../../Collections/List%20View.md">List View</a> is supported, allowing to print multiple notes at once while preserving hierarchy (similar to a book).
* <a class="reference-link" href="../../Collections/Presentation.md">Presentation</a> is also supported, where each slide/subnote is displayed.
* The rest of the collections are not supported, but we plan to add support for all the collection types at some point.
* Using <a class="reference-link" href="../../Theme%20development/Custom%20app-wide%20CSS.md">Custom app-wide CSS</a> for printing is not longer supported, due to a more stable but isolated mechanism.
* We plan to introduce a new mechanism specifically for a print CSS.

View File

@ -9,3 +9,14 @@ In the example above, the "Node.js" note on the left panel contains several chil
* Each note can be expanded or collapsed by clicking on the arrow to the left of the title.
* In the <a class="reference-link" href="../Basic%20Concepts%20and%20Features/UI%20Elements/Ribbon.md">Ribbon</a>, in the _Collection_ tab there are options to expand and to collapse all notes easily.
## Printing and exporting to PDF
Since v0.100.0, list collections can be [printed or exported to PDF](../Basic%20Concepts%20and%20Features/Notes/Printing%20%26%20Exporting%20as%20PDF.md).
A printed list collection will print all the notes in the collection, in the right order and preserving the full hierarchy.
If exported to PDF within the desktop application, there is additional functionality:
* The table of contents of the PDF will reflect the structure of the notes.
* Reference and inline links to other notes within the same hierarchy will be functional (will jump to the corresponding page). If a link refers to a note that is not in the printed hierarchy, it will be unlinked.