Simple content library for icon packs (#8650)
Some checks are pending
Checks / main (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Deploy Documentation / Build and Deploy Documentation (push) Waiting to run
Dev / Test development (push) Waiting to run
Dev / Build Docker image (push) Blocked by required conditions
Dev / Check Docker build (Dockerfile) (push) Blocked by required conditions
Dev / Check Docker build (Dockerfile.alpine) (push) Blocked by required conditions
/ Check Docker build (Dockerfile) (push) Waiting to run
/ Check Docker build (Dockerfile.alpine) (push) Waiting to run
/ Build Docker images (Dockerfile, ubuntu-24.04-arm, linux/arm64) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.alpine, ubuntu-latest, linux/amd64) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.legacy, ubuntu-24.04-arm, linux/arm/v7) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.legacy, ubuntu-24.04-arm, linux/arm/v8) (push) Blocked by required conditions
/ Merge manifest lists (push) Blocked by required conditions
playwright / E2E tests on linux-arm64 (push) Waiting to run
playwright / E2E tests on linux-x64 (push) Waiting to run
Deploy website / Build & deploy website (push) Waiting to run

This commit is contained in:
Elian Doran 2026-02-07 16:52:08 +02:00 committed by GitHub
commit 2cd3d4bfb7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 709 additions and 482 deletions

View File

@ -285,10 +285,11 @@ function getRenderingType(entity: FNote | FAttachment) {
} }
const mime = "mime" in entity && entity.mime; const mime = "mime" in entity && entity.mime;
const isIconPack = entity instanceof FNote && entity.hasLabel("iconPack");
if (type === "file" && mime === "application/pdf") { if (type === "file" && mime === "application/pdf") {
type = "pdf"; type = "pdf";
} else if ((type === "file" || type === "viewConfig") && mime && CODE_MIME_TYPES.has(mime)) { } else if ((type === "file" || type === "viewConfig") && mime && CODE_MIME_TYPES.has(mime) && !isIconPack) {
type = "code"; type = "code";
} else if (type === "file" && mime && mime.startsWith("audio/")) { } else if (type === "file" && mime && mime.startsWith("audio/")) {
type = "audio"; type = "audio";

View File

@ -206,6 +206,7 @@ span.fancytree-selected .fancytree-title {
} }
span.fancytree-selected .fancytree-custom-icon::before { span.fancytree-selected .fancytree-custom-icon::before {
font-family: "boxicons";
content: "\eb43"; content: "\eb43";
border: 1px solid var(--main-border-color); border: 1px solid var(--main-border-color);
border-radius: 3px; border-radius: 3px;

View File

@ -1 +0,0 @@
*.zip

View File

@ -1,5 +1,5 @@
import { createWriteStream, mkdirSync } from "node:fs"; import { createWriteStream, mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path"; import { join, resolve } from "node:path";
import cls from "@triliumnext/server/src/services/cls.js"; import cls from "@triliumnext/server/src/services/cls.js";
@ -13,7 +13,8 @@ process.env.TRILIUM_RESOURCE_DIR = "../server/src";
process.env.NODE_ENV = "development"; process.env.NODE_ENV = "development";
async function main() { async function main() {
const outputDir = join(__dirname, "output"); const outputDir = join(__dirname, "../../website/public/resources/icon-packs");
const outputMetaDir = join(__dirname, "../../website/src/resources/icon-packs");
mkdirSync(outputDir, { recursive: true }); mkdirSync(outputDir, { recursive: true });
const i18n = await import("@triliumnext/server/src/services/i18n.js"); const i18n = await import("@triliumnext/server/src/services/i18n.js");
@ -49,7 +50,8 @@ async function main() {
}); });
// Export to zip. // Export to zip.
const zipFilePath = join(outputDir, `${iconPack.name}.zip`); const zipFileName = `${iconPack.name}.zip`;
const zipFilePath = join(outputDir, zipFileName);
const fileOutputStream = createWriteStream(zipFilePath); const fileOutputStream = createWriteStream(zipFilePath);
const { exportToZip } = (await import("@triliumnext/server/src/services/export/zip.js")).default; const { exportToZip } = (await import("@triliumnext/server/src/services/export/zip.js")).default;
const taskContext = new (await import("@triliumnext/server/src/services/task_context.js")).default( const taskContext = new (await import("@triliumnext/server/src/services/task_context.js")).default(
@ -58,7 +60,15 @@ async function main() {
await exportToZip(taskContext, branch, "html", fileOutputStream, false, { skipExtraFiles: true }); await exportToZip(taskContext, branch, "html", fileOutputStream, false, { skipExtraFiles: true });
await new Promise<void>((resolve) => { fileOutputStream.on("finish", resolve); }); await new Promise<void>((resolve) => { fileOutputStream.on("finish", resolve); });
console.log(`Built icon pack: ${iconPack.name} (${zipFilePath})`); // Save meta.
const metaFilePath = join(outputMetaDir, `${iconPack.name}.json`);
writeFileSync(metaFilePath, JSON.stringify({
name: iconPack.name,
file: zipFileName,
...iconPack.meta
}, null, 2));
console.log(`Built icon pack ${iconPack.name}.`);
} }
const builtIconPacks = [ const builtIconPacks = [
@ -69,6 +79,8 @@ async function main() {
phosphor("fill") phosphor("fill")
]; ];
await Promise.all(builtIconPacks.map(buildIconPack)); await Promise.all(builtIconPacks.map(buildIconPack));
console.log(`\n✅ Built icon packs are available at ${resolve(outputDir)}.`);
} }
cls.init(() => { cls.init(() => {

View File

@ -9,5 +9,10 @@ export interface IconPackData {
name: string; name: string;
mime: string; mime: string;
content: Buffer; content: Buffer;
},
meta: {
version: string;
website: string;
description: string;
} }
} }

View File

@ -29,7 +29,7 @@ export default function buildIcons(pack: "basic" | "brands"): IconPackData {
return { return {
name: pack === "basic" ? "Boxicons 3 (Basic)" : "Boxicons 3 (Brands)", name: pack === "basic" ? "Boxicons 3 (Basic)" : "Boxicons 3 (Brands)",
prefix: pack === "basic" ? "bx3" : "bxl3", prefix: pack === "basic" ? "bx3" : "bxl3",
icon: pack === "basic" ? "bx3 bx-cube" : "bxl3 bxl-boxicons", icon: pack === "basic" ? "bx3 bx-cube" : "bxl3 bx-boxicons",
fontFile: { fontFile: {
name: `${fileName}.woff2`, name: `${fileName}.woff2`,
mime: "font/woff2", mime: "font/woff2",
@ -37,6 +37,13 @@ export default function buildIcons(pack: "basic" | "brands"): IconPackData {
}, },
manifest: { manifest: {
icons icons
},
meta: {
version: "3.0.0",
website: "https://boxicons.com/",
description: pack === "basic"
? "The Basic set of icons from Boxicons v3. This is an upgrade from Trilium's built-in icon pack (Boxicons v2)."
: "The brand set of icons from Boxicons v3."
} }
}; };
} }

View File

@ -7,6 +7,7 @@ import { extractClassNamesFromCss, getModulePath } from "../utils";
export default function buildIcons(): IconPackData { export default function buildIcons(): IconPackData {
const baseDir = getModulePath("@mdi/font"); const baseDir = getModulePath("@mdi/font");
const packageJson = JSON.parse(readFileSync(join(baseDir, "package.json"), "utf-8"));
const cssFilePath = join(baseDir, "css", "materialdesignicons.min.css"); const cssFilePath = join(baseDir, "css", "materialdesignicons.min.css");
const cssFileContent = readFileSync(cssFilePath, "utf-8"); const cssFileContent = readFileSync(cssFilePath, "utf-8");
@ -21,6 +22,11 @@ export default function buildIcons(): IconPackData {
name: "materialdesignicons-webfont.woff2", name: "materialdesignicons-webfont.woff2",
mime: "font/woff2", mime: "font/woff2",
content: readFileSync(join(baseDir, "fonts", "materialdesignicons-webfont.woff2")) content: readFileSync(join(baseDir, "fonts", "materialdesignicons-webfont.woff2"))
},
meta: {
version: packageJson.version,
website: "https://pictogrammers.com/library/mdi/",
description: "The community Material Design Icons pack (@mdi/font). Not to be confused with Google's own Material Design Icons."
} }
}; };
} }

View File

@ -5,7 +5,9 @@ import { IconPackData } from "../provider";
import { getModulePath } from "../utils"; import { getModulePath } from "../utils";
export default function buildIcons(packName: "regular" | "fill"): IconPackData { export default function buildIcons(packName: "regular" | "fill"): IconPackData {
const baseDir = join(getModulePath("@phosphor-icons/web"), "src", packName); const moduleDir = getModulePath("@phosphor-icons/web");
const baseDir = join(moduleDir, "src", packName);
const packageJson = JSON.parse(readFileSync(join(moduleDir, "package.json"), "utf-8"));
const iconIndex = JSON.parse(readFileSync(join(baseDir, "selection.json"), "utf-8")); const iconIndex = JSON.parse(readFileSync(join(baseDir, "selection.json"), "utf-8"));
const icons: IconPackData["manifest"]["icons"] = {}; const icons: IconPackData["manifest"]["icons"] = {};
@ -41,6 +43,13 @@ export default function buildIcons(packName: "regular" | "fill"): IconPackData {
name: fontFile!, name: fontFile!,
mime: "font/woff2", mime: "font/woff2",
content: readFileSync(join(baseDir, fontFile!)) content: readFileSync(join(baseDir, fontFile!))
},
meta: {
version: packageJson.version,
website: "https://phosphoricons.com/",
description: packName === "regular"
? "The regular weight version of Phosphor Icons."
: "The filled version of Phosphor Icons."
} }
}; };
} }

View File

@ -1,10 +1,10 @@
<p>Frequently used notes can be bookmarked, which will make them appear in <p>Frequently used notes can be bookmarked, which will make them appear in
the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_xYmIYSP6wE3F">Launch Bar</a>&nbsp;for the&nbsp;<a class="reference-link" href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>&nbsp;for
easy access.</p> easy access.</p>
<h2>Configuring the launch bar</h2> <h2>Configuring the launch bar</h2>
<p>If bookmarks don't appear in the launch bar, then most likely the bookmark <p>If bookmarks don't appear in the launch bar, then most likely the bookmark
section has been hidden. Go to the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_xYmIYSP6wE3F">Launch Bar</a>&nbsp;configuration section has been hidden. Go to the&nbsp;<a class="reference-link" href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>&nbsp;configuration
from the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_x3i7MxGccDuM">Global menu</a>&nbsp;and from the&nbsp;<a class="reference-link" href="#root/_help_x3i7MxGccDuM">Global menu</a>&nbsp;and
ensure <em>Bookmarks</em> is in the <em>Visible Launchers</em> section.</p> ensure <em>Bookmarks</em> is in the <em>Visible Launchers</em> section.</p>
<h2>Bookmark folder</h2> <h2>Bookmark folder</h2>
<p>Space in the left panel is limited, and you might want to bookmark many <p>Space in the left panel is limited, and you might want to bookmark many

View File

@ -1,73 +1,85 @@
<h2>Importing an existing icon pack</h2> <figure class="image image-style-align-right image_resized" style="width:45.14%;">
<img style="aspect-ratio:854/649;" src="Icon Packs_image.png"
width="854" height="649">
</figure>
<p>By default, Trilium comes with a set of icons called Boxicons v2. Since
v0.102.0, custom icon packs allow a wider selection of icons for notes.</p>
<p>Icon packs are specific to Trilium, so they must either be created from <p>Icon packs are specific to Trilium, so they must either be created from
scratch (see below) or imported from a ZIP file from a third-party developer.</p> scratch (see below) or imported from a ZIP file from a third-party developer.</p>
<aside <h2>Sample icon packs</h2>
class="admonition note"> <p>The Trilium team maintains a few icon packs that are not shipped with
Trilium. These icon packs can be found on the official website on the
<a
href="https://triliumnotes.org/resources">Resources page</a>.</p>
<h2>Importing an existing icon pack</h2>
<aside class="admonition note">
<p><strong>Icon packs are third-party content</strong> <p><strong>Icon packs are third-party content</strong>
</p> </p>
<p>The Trilium maintainers are not responsible for keeping these icon packs <p>Apart from the <a href="https://triliumnotes.org/resources">sample icon packs</a>,
up to date. If you have an issue with a specific icon pack, then the issue the Trilium maintainers are not responsible for keeping icon packs up to
must be reported to the third-party developer responsible for it, not the date. If you have an issue with a specific icon pack, then the issue must
Trilium team.</p> be reported to the third-party developer responsible for it, not the Trilium
</aside> team.</p>
<p>To import an icon pack:</p> </aside>
<ol> <p>To import an icon pack:</p>
<li>Ideally, create a dedicated spot in your note tree where to place the <ol>
icon packs.</li> <li>Ideally, create a dedicated spot in your note tree where to place the
<li>Right click the note where to put it and select <em>Import into note</em>.</li> icon packs.</li>
<li>Uncheck <em>Safe import</em>.</li> <li>Right click the note where to put it and select <em>Import into note</em>.</li>
<li
>Uncheck <em>Safe import</em>.</li>
<li>Select <em>Import</em>.</li> <li>Select <em>Import</em>.</li>
<li><a href="#root/_help_s8alTXmpFR61">Refresh the application</a>.</li> <li><a href="#root/_help_s8alTXmpFR61">Refresh the application</a>.</li>
</ol> </ol>
<aside class="admonition warning"> <aside class="admonition warning">
<p>Since <em>Safe import</em> is disabled, make sure you trust the source as <p>Since <em>Safe import</em> is disabled, make sure you trust the source as
it could contain dangerous third-party scripts. One good way to check if it could contain dangerous third-party scripts. One good way to check if
the icon pack is safe is to manually extract the .zip and inspect the file the icon pack is safe is to manually extract the .zip and inspect the file
contents. Icon packs should only contain a font file and a JSON file. Other contents. Icon packs should only contain a font file and a JSON file. Other
files (especially scripts) are to be considered harmful.</p> files (especially scripts) are to be considered harmful.</p>
</aside> </aside>
<h2>Creating an icon pack</h2> <h2>Creating an icon pack</h2>
<p>Creating an icon pack requires some scripting knowledge outside Trilium <p>Creating an icon pack requires some scripting knowledge outside Trilium
in order to generate the list of icons. For information, see&nbsp;<a class="reference-link" in order to generate the list of icons. For information, see&nbsp;<a class="reference-link"
href="#root/_help_g1mlRoU8CsqC">Creating an icon pack</a>.</p> href="#root/_help_g1mlRoU8CsqC">Creating an icon pack</a>.</p>
<h2>Using an icon from an icon pack</h2> <h2>Using an icon from an icon pack</h2>
<p>After <a href="#root/_help_s8alTXmpFR61">refreshing the application</a>, the <p>After <a href="#root/_help_s8alTXmpFR61">refreshing the application</a>, the
icon pack should be enabled by default. To test this, simply select an icon pack should be enabled by default. To test this, simply select an
existing note or create a new one and try to change the note icon.</p> existing note or create a new one and try to change the note icon.</p>
<p>There should be a <em>Filter</em> button to the right of the search bar <p>There should be a <em>Filter</em> button to the right of the search bar
in the icon list. Clicking it allows filtering by icon pack and the newly in the icon list. Clicking it allows filtering by icon pack and the newly
imported icon pack should be displayed there.</p> imported icon pack should be displayed there.</p>
<aside class="admonition note"> <aside class="admonition note">
<p>If the icon pack is missing from that list, then most likely there's something <p>If the icon pack is missing from that list, then most likely there's something
wrong with it.</p> wrong with it.</p>
<ul> <ul>
<li>Try checking the&nbsp;<a class="reference-link" href="#root/_help_bnyigUA2UK7s">Backend (server) logs</a>&nbsp;for <li>Try checking the&nbsp;<a class="reference-link" href="#root/_help_bnyigUA2UK7s">Backend (server) logs</a>&nbsp;for
clues and make sure that the icon pack has the <code spellcheck="false">#iconPack</code> clues and make sure that the icon pack has the <code spellcheck="false">#iconPack</code>
<a <a
href="#root/_help_HI6GBBIduIgv">label</a>with a value assigned to it (a prefix).</li> href="#root/_help_HI6GBBIduIgv">label</a>with a value assigned to it (a prefix).</li>
<li>Icon packs that are <a href="#root/_help_bwg0e8ewQMak">protected</a> are ignored.</li> <li>Icon packs that are <a href="#root/_help_bwg0e8ewQMak">protected</a> are ignored.</li>
</ul> </ul>
</aside> </aside>
<h2>Integration with the share and export functionality</h2> <h2>Integration with the share and export functionality</h2>
<p>Custom icon packs are also supported by the&nbsp;<a class="reference-link" <p>Custom icon packs are also supported by the&nbsp;<a class="reference-link"
href="#root/_help_R9pX4DGra2Vt">Sharing</a>&nbsp;feature, where they will be href="#root/_help_R9pX4DGra2Vt">Sharing</a>&nbsp;feature, where they will be
shown in the note tree. However, in order for an icon pack to be visible shown in the note tree. However, in order for an icon pack to be visible
to the share function, the icon pack note must also be shared.</p> to the share function, the icon pack note must also be shared.</p>
<p>If you are using a custom share theme, make sure it supports the <p>If you are using a custom share theme, make sure it supports the
<code <code
spellcheck="false">iconPackCss</code>, otherwise icons will not show up. Check the original spellcheck="false">iconPackCss</code>, otherwise icons will not show up. Check the original
share template source code for reference.</p> share template source code for reference.</p>
<p>Custom icon packs will also be preserved when&nbsp;<a class="reference-link" <p>Custom icon packs will also be preserved when&nbsp;<a class="reference-link"
href="#root/_help_ycBFjKrrwE9p">Exporting static HTML for web publishing</a>. href="#root/_help_ycBFjKrrwE9p">Exporting static HTML for web publishing</a>.
In this case, there's no requirement to make the icon pack shared.</p> In this case, there's no requirement to make the icon pack shared.</p>
<h2>What happens if I remove an icon pack</h2> <h2>What happens if I remove an icon pack</h2>
<p>If an icon pack is removed or disabled (by removing or altering its <p>If an icon pack is removed or disabled (by removing or altering its
<code <code
spellcheck="false">#iconPack</code>label), all the notes that use this icon pack will show spellcheck="false">#iconPack</code>label), all the notes that use this icon pack will show
in the&nbsp;<a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>&nbsp;with in the&nbsp;<a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>&nbsp;with
no icon. This won't cause any issues apart from looking strange.</p> no icon. This won't cause any issues apart from looking strange.</p>
<p>The solution is to replace the icons with some else, try using&nbsp; <p>The solution is to replace the icons with some else, try using&nbsp;
<a <a
class="reference-link" href="#root/_help_eIg8jdvaoNNd">Search</a>&nbsp;which supports bulk actions, to identify the notes with class="reference-link" href="#root/_help_eIg8jdvaoNNd">Search</a>&nbsp;which supports bulk actions, to identify the notes with
the now deleted icon pack (by looking for the prefix) and changing or removing the now deleted icon pack (by looking for the prefix) and changing or removing
their <code spellcheck="false">iconClass</code>.</p> their <code spellcheck="false">iconClass</code>.</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

View File

@ -1,7 +1,7 @@
<h2>Position of the Launch bar</h2> <h2>Position of the Launch bar</h2>
<p>On desktop, depending on the layout selected, the launcher bar will either <p>On desktop, depending on the layout selected, the launcher bar will either
be on the left side of the screen with buttons displayed vertically or be on the left side of the screen with buttons displayed vertically or
at the top of the screen. See&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_x0JgW8UqGXvq">Vertical and horizontal layout</a>&nbsp;for at the top of the screen. See&nbsp;<a class="reference-link" href="#root/_help_x0JgW8UqGXvq">Vertical and horizontal layout</a>&nbsp;for
more information.</p> more information.</p>
<p>On mobile, the launch bar will always be at the bottom.</p> <p>On mobile, the launch bar will always be at the bottom.</p>
<p>If there are too many items in the launch bar to fit the screen, it will <p>If there are too many items in the launch bar to fit the screen, it will
@ -21,10 +21,10 @@
<li>Right click in the empty space between launchers on the launch bar and <li>Right click in the empty space between launchers on the launch bar and
select <em>Configure Launchbar.</em> select <em>Configure Launchbar.</em>
</li> </li>
<li>Click on the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_x3i7MxGccDuM">Global menu</a>&nbsp;and <li>Click on the&nbsp;<a class="reference-link" href="#root/_help_x3i7MxGccDuM">Global menu</a>&nbsp;and
select <em>Configure Launchbar</em>.</li> select <em>Configure Launchbar</em>.</li>
</ul> </ul>
<p>This will open a new tab with the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_oPVyFC7WL2Lp">Note Tree</a>&nbsp;listing <p>This will open a new tab with the&nbsp;<a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>&nbsp;listing
the launchers.</p> the launchers.</p>
<p> <p>
<img src="Launch Bar_image.png"> <img src="Launch Bar_image.png">
@ -37,11 +37,10 @@
one. The reasoning is that not all desktop icons are available on mobile, one. The reasoning is that not all desktop icons are available on mobile,
and fewer icons fit on a mobile screen.</p> and fewer icons fit on a mobile screen.</p>
<p>To configure the launch bar on mobile, go to&nbsp;<a class="reference-link" <p>To configure the launch bar on mobile, go to&nbsp;<a class="reference-link"
href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_x3i7MxGccDuM">Global menu</a>&nbsp;and href="#root/_help_x3i7MxGccDuM">Global menu</a>&nbsp;and select <em>Configure Launchbar</em>.</p>
select <em>Configure Launchbar</em>.</p>
<p>The configure the mobile launch bar while on the desktop (especially useful <p>The configure the mobile launch bar while on the desktop (especially useful
to configure more complicated launchers such as scripts or custom widgets), to configure more complicated launchers such as scripts or custom widgets),
go to&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_x3i7MxGccDuM">Global menu</a>&nbsp; go to&nbsp;<a class="reference-link" href="#root/_help_x3i7MxGccDuM">Global menu</a>&nbsp;
Advanced → Show Hidden Subtree and look for the <em>Mobile Launch Bar</em> section. Advanced → Show Hidden Subtree and look for the <em>Mobile Launch Bar</em> section.
While in the hidden subtree, it's also possible to drag launchers between While in the hidden subtree, it's also possible to drag launchers between
the <em>Mobile Launch Bar</em> and (Desktop) <em>Launch Bar</em> sections.</p> the <em>Mobile Launch Bar</em> and (Desktop) <em>Launch Bar</em> sections.</p>
@ -52,13 +51,13 @@
<p>Similarly, to remove it from the launch bar, simply look for it in <em>Visible Launchers</em> then <p>Similarly, to remove it from the launch bar, simply look for it in <em>Visible Launchers</em> then
right click it and select <em>Move to available launchers</em> or use drag-and-drop.</p> right click it and select <em>Move to available launchers</em> or use drag-and-drop.</p>
<p>Drag-and-drop the items in the&nbsp;tree&nbsp;in order to change their <p>Drag-and-drop the items in the&nbsp;tree&nbsp;in order to change their
order. See&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_oPVyFC7WL2Lp">Note Tree</a>&nbsp;for order. See&nbsp;<a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>&nbsp;for
more interaction options, including using keyboard shortcuts.</p> more interaction options, including using keyboard shortcuts.</p>
<h2>Customizing the launcher</h2> <h2>Customizing the launcher</h2>
<ul> <ul>
<li>The icon of a launcher can be changed just like a normal note. See&nbsp; <li>The icon of a launcher can be changed just like a normal note. See&nbsp;
<a <a
class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/BFs8mudNFgCS/_help_p9kXRFAkwN4o">Note Icons</a>&nbsp;for more information.</li> class="reference-link" href="#root/_help_p9kXRFAkwN4o">Note Icons</a>&nbsp;for more information.</li>
<li>The title of the launcher can also be changed.</li> <li>The title of the launcher can also be changed.</li>
</ul> </ul>
<h3>Resetting</h3> <h3>Resetting</h3>
@ -70,39 +69,43 @@
<p>Right click either the <em>Available launchers</em> or <em>Visible launchers</em> sections <p>Right click either the <em>Available launchers</em> or <em>Visible launchers</em> sections
and select one of the options:</p> and select one of the options:</p>
<ol> <ol>
<li><strong>Note Launcher</strong> <li>
<br>A note launcher will simply navigate to a specified note. <p><strong>Note Launcher</strong>
<br>A note launcher will simply navigate to a specified note.</p>
<ol> <ol>
<li>Set the <code spellcheck="false">target</code> promoted attribute to the <li>Set the <code spellcheck="false">target</code> promoted attribute to the
note to navigate to.</li> note to navigate to.</li>
<li>Optionally, set <code spellcheck="false">hoistedNote</code> to hoist a particular <li>Optionally, set <code spellcheck="false">hoistedNote</code> to hoist a particular
note. See&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/wArbEsdSae6g/_help_OR8WJ7Iz9K4U">Note Hoisting</a>&nbsp;for note. See&nbsp;<a class="reference-link" href="#root/_help_OR8WJ7Iz9K4U">Note Hoisting</a>&nbsp;for
more information.</li> more information.</li>
<li>Optionally, set a <code spellcheck="false">keyboardShortcut</code> to trigger <li>Optionally, set a <code spellcheck="false">keyboardShortcut</code> to trigger
the launcher.</li> the launcher.</li>
</ol> </ol>
</li> </li>
<li><strong>Script Launcher</strong> <li>
<br>An advanced launcher which will run a script upon pressing. See&nbsp; <p><strong>Script Launcher</strong>
<a <br>An advanced launcher which will run a script upon pressing. See&nbsp;
class="reference-link" href="#root/pOsGYCXsbNQG/_help_CdNpE2pqjmI6">Scripting</a>&nbsp;for more information. <a
<ol> class="reference-link" href="#root/_help_CdNpE2pqjmI6">Scripting</a>&nbsp;for more information.</p>
<li>Set <code spellcheck="false">script</code> to point to the desired script <ol>
to run.</li> <li>Set <code spellcheck="false">script</code> to point to the desired script
<li>Optionally, set a <code spellcheck="false">keyboardShortcut</code> to trigger to run.</li>
the launcher.</li> <li>Optionally, set a <code spellcheck="false">keyboardShortcut</code> to trigger
</ol> the launcher.</li>
</ol>
</li> </li>
<li class="ck-list-marker-bold"> <li>
<p><strong>Custom Widget</strong> <p><strong>Custom Widget</strong>
</p> </p>
<p>Allows defining a custom widget to be rendered inside the launcher. See&nbsp; <p>Allows defining a custom widget to be rendered inside the launcher. See&nbsp;
<a <a
class="reference-link" href="#root/pOsGYCXsbNQG/CdNpE2pqjmI6/yIhgI5H7A2Sm/MgibgPcfeuGz/_help_SynTBQiBsdYJ">Widget Basics</a>&nbsp;for more information.</p> class="reference-link" href="#root/_help_SynTBQiBsdYJ">Widget Basics</a>&nbsp;for more information.</p>
</li>
<li>
<p><strong>Spacers</strong>
<br>Launchers that create some distance between other launchers for better
visual distinction.</p>
</li> </li>
<li><strong>Spacers</strong>
<br>Launchers that create some distance between other launchers for better
visual distinction.</li>
</ol> </ol>
<p>Launchers are configured via predefined&nbsp;<a class="reference-link" <p>Launchers are configured via predefined&nbsp;<a class="reference-link"
href="#root/pOsGYCXsbNQG/tC7s2alapj8V/zEY4DaJG4YT5/_help_OFXdgB2nNk1F">Promoted Attributes</a>.</p> href="#root/_help_OFXdgB2nNk1F">Promoted Attributes</a>.</p>

View File

@ -4,7 +4,8 @@ class="image image-style-align-center">
<img style="aspect-ratio:1398/1015;" src="Split View_2_Split View_im.png" <img style="aspect-ratio:1398/1015;" src="Split View_2_Split View_im.png"
width="1398" height="1015"> width="1398" height="1015">
</figure> </figure>
<h2><strong>Interactions</strong></h2>
<h2><strong>Interactions</strong></h2>
<ul> <ul>
<li>Press the <li>Press the
<img src="Split View_Split View_imag.png">button to the right of a note's title to open a new split to the right <img src="Split View_Split View_imag.png">button to the right of a note's title to open a new split to the right
@ -50,12 +51,11 @@ class="image image-style-align-center">
<ul> <ul>
<li>On smartphones, the split views are laid out vertically (one on the top <li>On smartphones, the split views are laid out vertically (one on the top
and one on the bottom), instead of horizontally as on the desktop.</li> and one on the bottom), instead of horizontally as on the desktop.</li>
<li <li>There can be only one split open per tab.</li>
>There can be only one split open per tab.</li> <li>It's not possible to resize the two split panes.</li>
<li>It's not possible to resize the two split panes.</li> <li>When the keyboard is opened, the active note will be “maximized”, thus
<li>When the keyboard is opened, the active note will be “maximized”, thus allowing for more space even when a split is open. When the keyboard is
allowing for more space even when a split is open. When the keyboard is closed, the splits become equal in size again.</li>
closed, the splits become equal in size again.</li>
</ul> </ul>
<p>Interaction:</p> <p>Interaction:</p>
<ul> <ul>

View File

@ -8,8 +8,7 @@
<ul> <ul>
<li>For the vertical layout, the tabs will be placed at the top but to the <li>For the vertical layout, the tabs will be placed at the top but to the
right of the&nbsp;<a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>.</li> right of the&nbsp;<a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>.</li>
<li <li>For the horizontal layout, the tabs will be placed at the top in full-width,
>For the horizontal layout, the tabs will be placed at the top in full-width,
above the&nbsp;<a href="#root/_help_oPVyFC7WL2Lp">note tree</a>, allowing for above the&nbsp;<a href="#root/_help_oPVyFC7WL2Lp">note tree</a>, allowing for
more tabs to be comfortably displayed.</li> more tabs to be comfortably displayed.</li>
</ul> </ul>
@ -23,8 +22,7 @@
href="#root/_help_luNhaphA37EO">Split View</a>. Each tab can have one or more href="#root/_help_luNhaphA37EO">Split View</a>. Each tab can have one or more
notes, displayed horizontally.</li> notes, displayed horizontally.</li>
<li>Tabs can be reordered by drag-and-dropping it into a new position.</li> <li>Tabs can be reordered by drag-and-dropping it into a new position.</li>
<li <li>An existing tab can be displayed in a new window by dragging the tab upwards
>An existing tab can be displayed in a new window by dragging the tab upwards
or downwards. It is not possible to combine tabs back into another window.</li> or downwards. It is not possible to combine tabs back into another window.</li>
</ul> </ul>
<h2>Keyboard interaction</h2> <h2>Keyboard interaction</h2>
@ -47,20 +45,20 @@
<img style="aspect-ratio:1242/2688;" src="Tabs_IMG_1767.PNG" <img style="aspect-ratio:1242/2688;" src="Tabs_IMG_1767.PNG"
width="1242" height="2688"> width="1242" height="2688">
</figure> </figure>
<p>Tabs are also supported on the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/Otzi9La2YAUX/_help_RDslemsQ6gCp">Mobile Frontend</a>.</p> <p>Tabs are also supported on the&nbsp;<a class="reference-link" href="#root/_help_RDslemsQ6gCp">Mobile Frontend</a>.</p>
<p>Since v0.102.0, the tabs are displayed by pressing the dedicated tab switcher <p>Since v0.102.0, the tabs are displayed by pressing the dedicated tab switcher
button in the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_xYmIYSP6wE3F">Launch Bar</a>. button in the&nbsp;<a class="reference-link" href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>.
In this view the tabs are laid out on a grid with a preview of the note In this view the tabs are laid out on a grid with a preview of the note
content.</p> content.</p>
<p>The context menu button at the top-right of the popup allows creating <p>The context menu button at the top-right of the popup allows creating
a new tab, reopening the last closed tab and closing all the tabs.</p> a new tab, reopening the last closed tab and closing all the tabs.</p>
<p><a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_luNhaphA37EO">Split View</a>s <p><a class="reference-link" href="#root/_help_luNhaphA37EO">Split View</a>s are
are also indicated in the tab switcher, with two titles displayed in a also indicated in the tab switcher, with two titles displayed in a tab.</p>
tab.</p> <aside
<aside class="admonition note"> class="admonition note">
<p>Versions prior to v0.102.0 also supported tabs, but they were displayed <p>Versions prior to v0.102.0 also supported tabs, but they were displayed
directly above the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_xYmIYSP6wE3F">Launch Bar</a>. directly above the&nbsp;<a class="reference-link" href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>.
The decision to use a more mobile-like tab switcher was taken because the The decision to use a more mobile-like tab switcher was taken because the
original tab bar could not support many tabs at once and the new design original tab bar could not support many tabs at once and the new design
better aligns with how mobile applications handle tabs.</p> better aligns with how mobile applications handle tabs.</p>
</aside> </aside>

View File

@ -14,36 +14,33 @@
and the mobile one:</p> and the mobile one:</p>
<ul> <ul>
<li> <li>
<p>The&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_oPVyFC7WL2Lp">Note Tree</a>&nbsp;is <p>The&nbsp;<a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>&nbsp;is
displayed as a sidebar. To display the sidebar, press the button in the displayed as a sidebar. To display the sidebar, press the button in the
top-left of the screen.</p> top-left of the screen.</p>
<ul> <ul>
<li> <li>There is also a swipe gesture that can be done from the left of the screen,
<p>There is also a swipe gesture that can be done from the left of the screen, but the browser's navigation gesture interferes with it most of the time
but the browser's navigation gesture interferes with it most of the time (depending on the platform).</li>
(depending on the platform).</p> <li>Press and hold a note to display the&nbsp;<a class="reference-link" href="#root/_help_YtSN43OrfzaA">Note tree contextual menu</a>.</li>
</li>
<li>
<p>Press and hold a note to display the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/oPVyFC7WL2Lp/_help_YtSN43OrfzaA">Note tree contextual menu</a>.</p>
</li>
</ul> </ul>
</li> </li>
<li>The&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/wArbEsdSae6g/_help_Ms1nauBra7gq">Quick search</a>&nbsp;bar
is also displayed at the top of the note tree.</li>
<li>The full&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/wArbEsdSae6g/_help_eIg8jdvaoNNd">Search</a>&nbsp;function
can be triggered either from either the&nbsp;<a class="reference-link"
href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_x3i7MxGccDuM">Global menu</a>&nbsp;or
from the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_xYmIYSP6wE3F">Launch Bar</a>,
if configured.</li>
<li> <li>
<p>The&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_xYmIYSP6wE3F">Launch Bar</a>&nbsp;is <p>The&nbsp;<a class="reference-link" href="#root/_help_Ms1nauBra7gq">Quick search</a>&nbsp;bar
is also displayed at the top of the note tree.</p>
</li>
<li>
<p>The full&nbsp;<a class="reference-link" href="#root/_help_eIg8jdvaoNNd">Search</a>&nbsp;function
can be triggered either from either the&nbsp;<a class="reference-link"
href="#root/_help_x3i7MxGccDuM">Global menu</a>&nbsp;or from the&nbsp;<a class="reference-link"
href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>, if configured.</p>
</li>
<li>
<p>The&nbsp;<a class="reference-link" href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>&nbsp;is
displayed at the bottom of the screen.</p> displayed at the bottom of the screen.</p>
<ul> <ul>
<li> <li>The launch bar uses a different configuration for icons than the desktop
<p>The launch bar uses a different configuration for icons than the desktop version. See the dedicated page for more information on how to configure
version. See the dedicated page for more information on how to configure it.</li>
it.</p>
</li>
</ul> </ul>
</li> </li>
<li> <li>
@ -51,18 +48,18 @@
on the top-right of the note.</p> on the top-right of the note.</p>
</li> </li>
<li> <li>
<p>The&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_3seOhtN8uLIY">Tabs</a>&nbsp;are <p>The&nbsp;<a class="reference-link" href="#root/_help_3seOhtN8uLIY">Tabs</a>&nbsp;are
grouped under a tab switcher in the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_xYmIYSP6wE3F">Launch Bar</a>, grouped under a tab switcher in the&nbsp;<a class="reference-link" href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>,
where the tabs are displayed in a full-screen grid with preview for easy where the tabs are displayed in a full-screen grid with preview for easy
switching, as well as additional options such as reopening closed tabs.</p> switching, as well as additional options such as reopening closed tabs.</p>
</li> </li>
<li> <li>
<p>Since v0.100.0,&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_luNhaphA37EO">Split View</a>&nbsp;can <p>Since v0.100.0,&nbsp;<a class="reference-link" href="#root/_help_luNhaphA37EO">Split View</a>&nbsp;can
also be used in mobile view, but with a maximum of two panes at once. The also be used in mobile view, but with a maximum of two panes at once. The
splits are displayed vertically instead of horizontally.</p> splits are displayed vertically instead of horizontally.</p>
</li> </li>
<li> <li>
<p>Starting with v0.102.0, the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_IjZS7iK5EXtb">New Layout</a>&nbsp;is <p>Starting with v0.102.0, the&nbsp;<a class="reference-link" href="#root/_help_IjZS7iK5EXtb">New Layout</a>&nbsp;is
enforced on mobile. This brings features such as the note badges, note enforced on mobile. This brings features such as the note badges, note
type switcher or collection properties which would otherwise not be available.</p> type switcher or collection properties which would otherwise not be available.</p>
</li> </li>
@ -74,17 +71,10 @@
<h3>On iOS with Safari</h3> <h3>On iOS with Safari</h3>
<ol> <ol>
<li>Open your default web browser and access your Trilium instance.</li> <li>Open your default web browser and access your Trilium instance.</li>
<li <li>Login.</li>
>Login.</li> <li>Press the […] button in the bottom-right of the screen and select Share.</li>
<li> <li>Scroll down to reveal the full list of items and choose “Add to Home Screen”.</li>
<p>Press the […] button in the bottom-right of the screen and select Share.</p> <li>Press “Add” and the web app will be available.</li>
</li>
<li>
<p>Scroll down to reveal the full list of items and choose “Add to Home Screen”.</p>
</li>
<li>
<p>Press “Add” and the web app will be available.</p>
</li>
</ol> </ol>
<h3>On Android with Google Chrome</h3> <h3>On Android with Google Chrome</h3>
<aside class="admonition important"> <aside class="admonition important">
@ -94,22 +84,13 @@
</aside> </aside>
<ol> <ol>
<li>Open your default web browser and access your Trilium instance.</li> <li>Open your default web browser and access your Trilium instance.</li>
<li <li>Login.</li>
>Login.</li> <li>Press the three vertical dots icon in the top-right of the screen and
<li> select <em>Add to Home screen.</em>
<p>Press the three vertical dots icon in the top-right of the screen and </li>
select <em>Add to Home screen.</em> <li>Select the <em>Install</em> option.</li>
</p> <li>Select an appropriate name.</li>
</li> <li>The web app will appear as an application, not on the home screen.</li>
<li>
<p>Select the <em>Install</em> option.</p>
</li>
<li>
<p>Select an appropriate name.</p>
</li>
<li>
<p>The web app will appear as an application, not on the home screen.</p>
</li>
</ol> </ol>
<h3>On Android with Brave</h3> <h3>On Android with Brave</h3>
<aside class="admonition important"> <aside class="admonition important">
@ -119,33 +100,19 @@
</aside> </aside>
<ol> <ol>
<li>Open your default web browser and access your Trilium instance.</li> <li>Open your default web browser and access your Trilium instance.</li>
<li <li>Login.</li>
>Login.</li> <li>Press the three vertical dots icon in the bottom-right of the screen and
<li> select <em>Add to Home screen</em>.</li>
<p>Press the three vertical dots icon in the bottom-right of the screen and <li>Press the <em>Install</em> option.</li>
select <em>Add to Home screen</em>.</p> <li>The web app will appear as an application, not on the home screen.</li>
</li>
<li>
<p>Press the <em>Install</em> option.</p>
</li>
<li>
<p>The web app will appear as an application, not on the home screen.</p>
</li>
</ol> </ol>
<h3>On Samsung Browser</h3> <h3>On Samsung Browser</h3>
<ol> <ol>
<li>Open your default web browser and access your Trilium instance.</li> <li>Open your default web browser and access your Trilium instance.</li>
<li <li>Login.</li>
>Login.</li> <li>Press the hamburger menu in the bottom-right of the screen.</li>
<li> <li>Select <em>Add to</em>, followed by <em>Home screen</em>.</li>
<p>Press the hamburger menu in the bottom-right of the screen.</p> <li>Press <em>Add</em> and the web app will appear on the home page.</li>
</li>
<li>
<p>Select <em>Add to</em>, followed by <em>Home screen</em>.</p>
</li>
<li>
<p>Press <em>Add</em> and the web app will appear on the home page.</p>
</li>
</ol> </ol>
<h2>Testing via the desktop application</h2> <h2>Testing via the desktop application</h2>
<p>If you are running Trilium without a dedicated <a href="#root/_help_WOcw2SLH6tbX">server installation</a>, <p>If you are running Trilium without a dedicated <a href="#root/_help_WOcw2SLH6tbX">server installation</a>,
@ -159,10 +126,10 @@
spellcheck="false">?desktop</code>query param on <strong>login</strong> page (Note: you might spellcheck="false">?desktop</code>query param on <strong>login</strong> page (Note: you might
need to log out).</p> need to log out).</p>
<p>Alternatively, simply select <em>Switch to Mobile/Desktop Version</em> in <p>Alternatively, simply select <em>Switch to Mobile/Desktop Version</em> in
the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_x3i7MxGccDuM">Global menu</a>.</p> the&nbsp;<a class="reference-link" href="#root/_help_x3i7MxGccDuM">Global menu</a>.</p>
<h2>Scripting</h2> <h2>Scripting</h2>
<p>You can alter the behavior with&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/_help_CdNpE2pqjmI6">Scripting</a>, <p>You can alter the behavior with&nbsp;<a class="reference-link" href="#root/_help_CdNpE2pqjmI6">Scripting</a>,
just like for normal frontend. For script notes to be executed, they need just like for normal frontend. For script notes to be executed, they need
to have labeled <code spellcheck="false">#run=mobileStartup</code>.</p> to have labeled <code spellcheck="false">#run=mobileStartup</code>.</p>
<p>Custom&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_xYmIYSP6wE3F">Launch Bar</a>&nbsp;widgets <p>Custom&nbsp;<a class="reference-link" href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>&nbsp;widgets
are also supported.</p> are also supported.</p>

View File

@ -1,7 +1,9 @@
import { ComponentChildren } from "preact";
import Icon from "./Icon.js";
import "./Button.css"; import "./Button.css";
import { ComponentChildren } from "preact";
import Icon from "./Icon.js";
interface LinkProps { interface LinkProps {
className?: string; className?: string;
href?: string; href?: string;
@ -9,6 +11,7 @@ interface LinkProps {
children?: ComponentChildren; children?: ComponentChildren;
title?: string; title?: string;
onClick?: (e: MouseEvent) => void; onClick?: (e: MouseEvent) => void;
download?: boolean;
} }
interface ButtonProps extends Omit<LinkProps, "children"> { interface ButtonProps extends Omit<LinkProps, "children"> {
@ -16,6 +19,7 @@ interface ButtonProps extends Omit<LinkProps, "children"> {
iconSvg?: string; iconSvg?: string;
text: ComponentChildren; text: ComponentChildren;
openExternally?: boolean; openExternally?: boolean;
download?: boolean;
outline?: boolean; outline?: boolean;
} }
@ -28,17 +32,18 @@ export default function Button({ iconSvg, text, className, outline, ...restProps
{iconSvg && <><Icon svg={iconSvg} />{" "}</>} {iconSvg && <><Icon svg={iconSvg} />{" "}</>}
<span class="text">{text}</span> <span class="text">{text}</span>
</Link> </Link>
) );
} }
export function Link({ openExternally, children, ...restProps }: LinkProps) { export function Link({ openExternally, children, download, ...restProps }: LinkProps) {
return ( return (
<a <a
{...restProps} {...restProps}
target={openExternally ? "_blank" : undefined} target={openExternally || download ? "_blank" : undefined}
rel={openExternally ? "noopener noreferrer" : undefined} download={download}
rel={openExternally || download ? "noopener noreferrer" : undefined}
> >
{children} {children}
</a> </a>
) );
} }

View File

@ -67,7 +67,7 @@ header {
} }
} }
@media (max-width: 719px) { @media (max-width: 719.99px) {
:root { :root {
--header-height: 60px; --header-height: 60px;
} }
@ -87,9 +87,17 @@ header {
.first-row { .first-row {
display: flex; display: flex;
justify-content: space-between;
align-items: center; align-items: center;
width: 100%; width: 100%;
gap: 2em;
.banner {
flex-grow: 1;
}
.social-button {
align-items: center;
}
} }
.menu-toggle { .menu-toggle {
@ -145,3 +153,36 @@ header {
} }
} }
} }
@media ((min-width: 720px) and (max-width: 1115px)) {
header {
.banner span {
display: none;
}
.content-wrapper {
width: 100%;
}
.repository-button .social-button {
flex-direction: column;
align-items: center;
font-size: 0.6em;
}
nav {
font-size: 0.9em;
justify-content: center;
}
.download-button {
font-size: 0.8em;
padding: 0.5em;
.text {
display: flex;
flex-direction: column;
}
}
}
}

View File

@ -1,16 +1,18 @@
import "./Header.css"; import "./Header.css";
import { Link } from "./Button.js";
import { SocialButtons, SocialButton } from "./Footer.js"; import { useContext, useEffect, useState } from "preact/hooks";
import { useContext, useEffect, useMemo, useState } from "preact/hooks";
import { useLocation } from 'preact-iso'; import { useLocation } from 'preact-iso';
import DownloadButton from './DownloadButton.js';
import githubIcon from "../assets/boxicons/bx-github.svg?raw";
import Icon from "./Icon.js";
import logoPath from "../assets/icon-color.svg";
import menuIcon from "../assets/boxicons/bx-menu.svg?raw";
import { LocaleContext } from "..";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { LocaleContext } from "..";
import githubIcon from "../assets/boxicons/bx-github.svg?raw";
import menuIcon from "../assets/boxicons/bx-menu.svg?raw";
import logoPath from "../assets/icon-color.svg";
import { swapLocaleInUrl } from "../i18n"; import { swapLocaleInUrl } from "../i18n";
import { Link } from "./Button.js";
import DownloadButton from './DownloadButton.js';
import { SocialButton,SocialButtons } from "./Footer.js";
import Icon from "./Icon.js";
interface HeaderLink { interface HeaderLink {
url: string; url: string;
@ -19,34 +21,34 @@ interface HeaderLink {
} }
export function Header(props: {repoStargazersCount: number}) { export function Header(props: {repoStargazersCount: number}) {
const { url } = useLocation(); const { url } = useLocation();
const { t } = useTranslation(); const { t } = useTranslation();
const locale = useContext(LocaleContext); const locale = useContext(LocaleContext);
const [ mobileMenuShown, setMobileMenuShown ] = useState(false); const [ mobileMenuShown, setMobileMenuShown ] = useState(false);
const [ headerLinks, setHeaderLinks ] = useState<HeaderLink[]>([]); const headerLinks = [
useEffect(() => { { url: "/get-started", text: t("header.get-started") },
setHeaderLinks([ { url: "/resources", text: t("header.resources") },
{ url: "/get-started", text: t("header.get-started") }, { url: "https://docs.triliumnotes.org/", text: t("header.documentation"), external: true },
{ url: "https://docs.triliumnotes.org/", text: t("header.documentation"), external: true }, { url: "/support-us", text: t("header.support-us") }
{ url: "/support-us", text: t("header.support-us") } ];
]);
}, [ locale, t ]);
return ( return (
<header> <header>
<div class="content-wrapper"> <div class="content-wrapper">
<div class="first-row"> <div class="first-row">
<a class="banner" href={`/${locale}/`}> <a class="banner" href={`/${locale}/`}>
<img src={logoPath} width="300" height="300" alt="Trilium Notes logo" />&nbsp;<span>Trilium Notes</span> <img src={logoPath} width="300" height="300" alt="Trilium Notes logo" />&nbsp;<span>Trilium Notes</span>
</a> </a>
<RepositoryButton repoStargazersCount={props.repoStargazersCount} className="mobile-only" />
<Link <Link
href="#" href="#"
className="mobile-only menu-toggle" className="mobile-only menu-toggle"
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
setMobileMenuShown(!mobileMenuShown) setMobileMenuShown(!mobileMenuShown);
}} }}
> >
<Icon svg={menuIcon} /> <Icon svg={menuIcon} />
@ -63,24 +65,31 @@ export function Header(props: {repoStargazersCount: number}) {
onClick={() => { onClick={() => {
setMobileMenuShown(false); setMobileMenuShown(false);
}} }}
>{link.text}</Link>) >{link.text}</Link>);
})} })}
<SocialButtons className="mobile-only" withText /> <SocialButtons className="mobile-only" withText />
</nav> </nav>
<div class="desktop-only repository-button"> <RepositoryButton repoStargazersCount={props.repoStargazersCount} className="desktop-only" />
<SocialButton
name="GitHub"
iconSvg={githubIcon}
counter={(props.repoStargazersCount / 1000).toFixed(1) + "K+"}
url="https://github.com/TriliumNext/Trilium"
/>
</div>
<DownloadButton /> <DownloadButton />
</div> </div>
</header> </header>
); );
}
function RepositoryButton({ repoStargazersCount, className }: {
repoStargazersCount: number;
className: string
}){
return (
<div class={`repository-button ${className}`}>
<SocialButton
name="GitHub"
iconSvg={githubIcon}
counter={`${(repoStargazersCount / 1000).toFixed(1)}K+`}
url="https://github.com/TriliumNext/Trilium"
/>
</div>
);
} }

View File

@ -6,23 +6,26 @@ let repoStargazersCount: number | null = null;
/** Returns the number of stargazers of the Trilium's GitHub repository. */ /** Returns the number of stargazers of the Trilium's GitHub repository. */
export async function getRepoStargazersCount() { export async function getRepoStargazersCount() {
if (repoStargazersCount === null) { if (repoStargazersCount === null) {
repoStargazersCount = await fetchRepoStargazersCount() && FALLBACK_STARGAZERS_COUNT; repoStargazersCount = await fetchRepoStargazersCount();
} }
return repoStargazersCount; return repoStargazersCount;
} }
async function fetchRepoStargazersCount(): Promise<number | null> { async function fetchRepoStargazersCount(): Promise<number> {
console.log("\nFetching stargazers count from GitHub API... "); console.log("\nFetching stargazers count from GitHub API... ");
const response = await fetch(API_URL); const response = await fetch(API_URL);
if (response.ok) { if (response.ok) {
const details = await response.json(); const details = await response.json();
if ("stargazers_count" in details) { const count = details["stargazers_count"];
return details["stargazers_count"];
}
}
console.error("Failed to fetch stargazers count from GitHub API:", response.status, response.statusText); if (typeof count === "number" && Number.isFinite(count) && count >= 0) {
return null; console.log(`Got number of stargazers: ${count}`);
return count;
}
}
console.error("Failed to fetch stargazers count from GitHub API:", response.status, response.statusText);
return FALLBACK_STARGAZERS_COUNT;
} }

View File

@ -1,71 +1,78 @@
import './style.css'; import './style.css';
import { FALLBACK_STARGAZERS_COUNT, getRepoStargazersCount } from './github-utils.js';
import { Header } from './components/Header.jsx'; import { changeLanguage } from 'i18next';
import { Home } from './pages/Home/index.jsx';
import { LocationProvider, Router, Route, hydrate, prerender as ssr, useLocation } from 'preact-iso';
import { NotFound } from './pages/_404.jsx';
import Footer from './components/Footer.js';
import GetStarted from './pages/GetStarted/get-started.js';
import SupportUs from './pages/SupportUs/SupportUs.js';
import { createContext } from 'preact'; import { createContext } from 'preact';
import { useLayoutEffect, useRef } from 'preact/hooks'; import { useLayoutEffect, useRef } from 'preact/hooks';
import { changeLanguage } from 'i18next'; import { hydrate, LocationProvider, prerender as ssr, Route, Router, useLocation } from 'preact-iso';
import Footer from './components/Footer.js';
import { Header } from './components/Header.jsx';
import { FALLBACK_STARGAZERS_COUNT, getRepoStargazersCount } from './github-utils';
import { extractLocaleFromUrl, initTranslations, LOCALES, mapLocale } from './i18n'; import { extractLocaleFromUrl, initTranslations, LOCALES, mapLocale } from './i18n';
import { NotFound } from './pages/_404.jsx';
import GetStarted from './pages/GetStarted/get-started.js';
import { Home } from './pages/Home/index.jsx';
import Resources from './pages/Resources/Resources';
import SupportUs from './pages/SupportUs/SupportUs.js';
export const LocaleContext = createContext('en'); export const LocaleContext = createContext('en');
export function App(props: {repoStargazersCount: number}) { export function App({ repoStargazersCount }) {
return ( return (
<LocationProvider> <LocationProvider>
<LocaleProvider> <LocaleProvider>
<Header repoStargazersCount={props.repoStargazersCount} /> <Header repoStargazersCount={repoStargazersCount ?? FALLBACK_STARGAZERS_COUNT} />
<main> <main>
<Router> <Router>
<Route path="/" component={Home} /> <Route path="/" component={Home} />
<Route path="/get-started" component={GetStarted} /> <Route path="/get-started" component={GetStarted} />
<Route path="/support-us" component={SupportUs} /> <Route path="/support-us" component={SupportUs} />
<Route path="/resources" component={Resources} />
<Route path="/:locale:/" component={Home} /> <Route path="/:locale:/" component={Home} />
<Route path="/:locale:/get-started" component={GetStarted} /> <Route path="/:locale:/get-started" component={GetStarted} />
<Route path="/:locale:/support-us" component={SupportUs} /> <Route path="/:locale:/support-us" component={SupportUs} />
<Route path="/:locale:/resources" component={Resources} />
<Route default component={NotFound} /> <Route default component={NotFound} />
</Router> </Router>
</main> </main>
<Footer /> <Footer />
</LocaleProvider> </LocaleProvider>
</LocationProvider> </LocationProvider>
); );
} }
export function LocaleProvider({ children }) { export function LocaleProvider({ children }) {
const { path } = useLocation(); const { path } = useLocation();
const localeId = getLocaleId(path); const localeId = getLocaleId(path);
const loadedRef = useRef(false); const loadedRef = useRef(false);
if (!loadedRef.current) { if (!loadedRef.current) {
initTranslations(localeId); initTranslations(localeId);
loadedRef.current = true; loadedRef.current = true;
} else { } else {
changeLanguage(localeId); changeLanguage(localeId);
} }
// Update html lang and dir attributes // Update html lang and dir attributes
useLayoutEffect(() => { useLayoutEffect(() => {
const correspondingLocale = LOCALES.find(l => l.id === localeId); const correspondingLocale = LOCALES.find(l => l.id === localeId);
document.documentElement.lang = localeId; document.documentElement.lang = localeId;
document.documentElement.dir = correspondingLocale?.rtl ? "rtl" : "ltr"; document.documentElement.dir = correspondingLocale?.rtl ? "rtl" : "ltr";
}, [localeId]); }, [localeId]);
return ( return (
<LocaleContext.Provider value={localeId}> <LocaleContext.Provider value={localeId}>
{children} {children}
</LocaleContext.Provider> </LocaleContext.Provider>
); );
} }
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
hydrate(<App repoStargazersCount={FALLBACK_STARGAZERS_COUNT} />, document.getElementById('app')!); const el = document.getElementById("prerender-data");
const data = JSON.parse(el?.innerText ?? "{}");
hydrate(<App {...data} />, document.getElementById('app')!);
} }
function getLocaleId(path: string) { function getLocaleId(path: string) {
@ -76,18 +83,18 @@ function getLocaleId(path: string) {
} }
export async function prerender(data) { export async function prerender(data) {
// Fetch the stargazer count of the Trilium's GitHub repo on prerender to pass // Fetch the stargazer count of the Trilium's GitHub repo on prerender to pass
// it to the App component for SSR. // it to the App component for SSR.
// This ensures the GitHub API is not called on every page load in the client. // This ensures the GitHub API is not called on every page load in the client.
const stargazersCount = await getRepoStargazersCount(); data.repoStargazersCount = await getRepoStargazersCount();
const { html, links } = await ssr(<App {...data} />);
const { html, links } = await ssr(<App repoStargazersCount={stargazersCount} {...data} />);
return { return {
html, html,
links, links,
data,
head: { head: {
lang: extractLocaleFromUrl(data.url) ?? "en" lang: extractLocaleFromUrl(data.url) ?? "en"
} }
} };
} }

View File

@ -104,7 +104,7 @@ section.hero-section {
} }
} }
@media (max-width: 719px) { @media (max-width: 719.99px) {
section.hero-section { section.hero-section {
padding-bottom: 0; padding-bottom: 0;
@ -214,7 +214,7 @@ section.faq {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@media (max-width: 719px) { @media (max-width: 719.99px) {
margin-top: 1em; margin-top: 1em;
} }
} }

View File

@ -0,0 +1,24 @@
.icon-packs .card-content {
.card-content-inner {
display: flex;
flex-direction: column;
flex-grow: 1;
}
.description {
font-size: 0.8em;
margin: 0;
flex-grow: 1;
height: 100%;
}
footer {
padding: 0;
border: 0;
display: flex;
align-items: center;
margin-top: 1em;
justify-content: space-between;
flex-grow: 1;
}
}

View File

@ -0,0 +1,55 @@
import "./Resources.css";
import { Trans, useTranslation } from "react-i18next";
import Button, { Link } from "../../components/Button";
import Card from "../../components/Card";
import Section from "../../components/Section";
import { usePageTitle } from "../../hooks";
interface IconPackMeta {
name: string;
file: string;
version: string;
website: string;
description: string;
}
const iconPacksMeta = Object.values(import.meta.glob("../../resources/icon-packs/*.json", {
eager: true
})) as IconPackMeta[];
export default function Resources() {
const { t } = useTranslation();
usePageTitle(t("resources.title"));
return (
<Section className="icon-packs fill">
<h2>{t("resources.icon_packs")}</h2>
<p>
<Trans
i18nKey="resources.icon_packs_intro"
components={{
DocumentationLink: <Link href="https://docs.triliumnotes.org/user-guide/concepts/themes/icon-packs" />
}}
/>
</p>
<div className="grid-3-cols">
{iconPacksMeta.map(meta => (
<Card
key={meta.name}
title={<>{meta.name} <small>{meta.version}</small></>}
>
<p className="description">{meta.description}</p>
<footer>
<Button href={`/resources/icon-packs/${encodeURIComponent(meta.file)}`} download text={t("resources.download")} />
<Link href={meta.website} openExternally>{t("resources.website")}</Link>
</footer>
</Card>
))}
</div>
</Section>
);
}

View File

@ -0,0 +1,7 @@
{
"name": "Boxicons 3 (Basic)",
"file": "Boxicons 3 (Basic).zip",
"version": "3.0.0",
"website": "https://boxicons.com/",
"description": "The Basic set of icons from Boxicons v3. This is an upgrade from Trilium's built-in icon pack (Boxicons v2)."
}

View File

@ -0,0 +1,7 @@
{
"name": "Boxicons 3 (Brands)",
"file": "Boxicons 3 (Brands).zip",
"version": "3.0.0",
"website": "https://boxicons.com/",
"description": "The brand set of icons from Boxicons v3."
}

View File

@ -0,0 +1,7 @@
{
"name": "Material Design Icons",
"file": "Material Design Icons.zip",
"version": "7.4.47",
"website": "https://pictogrammers.com/library/mdi/",
"description": "The community Material Design Icons pack (@mdi/font). Not to be confused with Google's own Material Design Icons."
}

View File

@ -0,0 +1,7 @@
{
"name": "Phosphor Icons (Fill)",
"file": "Phosphor Icons (Fill).zip",
"version": "2.1.2",
"website": "https://phosphoricons.com/",
"description": "The filled version of Phosphor Icons."
}

View File

@ -0,0 +1,7 @@
{
"name": "Phosphor Icons (Regular)",
"file": "Phosphor Icons (Regular).zip",
"version": "2.1.2",
"website": "https://phosphoricons.com/",
"description": "The regular weight version of Phosphor Icons."
}

View File

@ -42,7 +42,7 @@ body {
} }
main { main {
min-height: calc(100vh - 80px - 90px); min-height: calc(100vh - 80px - 136px);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
@ -65,6 +65,7 @@ a {
max-width: 1200px; max-width: 1200px;
width: 90%; width: 90%;
margin: auto; margin: auto;
box-sizing: border-box;
} }
section { section {
@ -149,7 +150,7 @@ img {
} }
} }
@media (max-width: 719px) { @media (max-width: 719.99px) {
.grid-4-cols > *, .grid-4-cols > *,
.grid-3-cols > *, .grid-3-cols > *,
.grid-2-cols > * { .grid-2-cols > * {

View File

@ -112,6 +112,7 @@
"header": { "header": {
"get-started": "Get started", "get-started": "Get started",
"documentation": "Documentation", "documentation": "Documentation",
"resources": "Resources",
"support-us": "Support us" "support-us": "Support us"
}, },
"footer": { "footer": {
@ -196,5 +197,12 @@
"description": "Trilium Notes hosted on PikaPods, a paid service for easy access and management. Not directly affiliated with the Trilium team.", "description": "Trilium Notes hosted on PikaPods, a paid service for easy access and management. Not directly affiliated with the Trilium team.",
"download_pikapod": "Set up on PikaPods", "download_pikapod": "Set up on PikaPods",
"download_triliumcc": "Alternatively see trilium.cc" "download_triliumcc": "Alternatively see trilium.cc"
},
"resources": {
"title": "Resources",
"icon_packs": "Icon packs",
"icon_packs_intro": "Expand the selection of available icons for your notes by using an icon pack. For more information about icon packs, see the <DocumentationLink>official documentation</DocumentationLink>.",
"download": "Download",
"website": "Website"
} }
} }

View File

@ -1,5 +1,5 @@
# Documentation # Documentation
There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/qIpo9UiTZNdm/Documentation_image.png" width="205" height="162"> There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/vAGCdo7kgoVD/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 _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. * The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers.

View File

@ -1480,17 +1480,87 @@
{ {
"type": "relation", "type": "relation",
"name": "internalLink", "name": "internalLink",
"value": "WOcw2SLH6tbX", "value": "oPVyFC7WL2Lp",
"isInheritable": false, "isInheritable": false,
"position": 10 "position": 10
}, },
{ {
"type": "relation", "type": "relation",
"name": "internalLink", "name": "internalLink",
"value": "nRqcgfTb97uV", "value": "YtSN43OrfzaA",
"isInheritable": false, "isInheritable": false,
"position": 20 "position": 20
}, },
{
"type": "relation",
"name": "internalLink",
"value": "Ms1nauBra7gq",
"isInheritable": false,
"position": 30
},
{
"type": "relation",
"name": "internalLink",
"value": "eIg8jdvaoNNd",
"isInheritable": false,
"position": 40
},
{
"type": "relation",
"name": "internalLink",
"value": "x3i7MxGccDuM",
"isInheritable": false,
"position": 50
},
{
"type": "relation",
"name": "internalLink",
"value": "xYmIYSP6wE3F",
"isInheritable": false,
"position": 60
},
{
"type": "relation",
"name": "internalLink",
"value": "3seOhtN8uLIY",
"isInheritable": false,
"position": 70
},
{
"type": "relation",
"name": "internalLink",
"value": "luNhaphA37EO",
"isInheritable": false,
"position": 80
},
{
"type": "relation",
"name": "internalLink",
"value": "IjZS7iK5EXtb",
"isInheritable": false,
"position": 90
},
{
"type": "relation",
"name": "internalLink",
"value": "WOcw2SLH6tbX",
"isInheritable": false,
"position": 100
},
{
"type": "relation",
"name": "internalLink",
"value": "nRqcgfTb97uV",
"isInheritable": false,
"position": 110
},
{
"type": "relation",
"name": "internalLink",
"value": "CdNpE2pqjmI6",
"isInheritable": false,
"position": 120
},
{ {
"type": "label", "type": "label",
"name": "shareAlias", "name": "shareAlias",
@ -1504,76 +1574,6 @@
"value": "bx bx-mobile-alt", "value": "bx bx-mobile-alt",
"isInheritable": false, "isInheritable": false,
"position": 60 "position": 60
},
{
"type": "relation",
"name": "internalLink",
"value": "x3i7MxGccDuM",
"isInheritable": false,
"position": 70
},
{
"type": "relation",
"name": "internalLink",
"value": "IjZS7iK5EXtb",
"isInheritable": false,
"position": 80
},
{
"type": "relation",
"name": "internalLink",
"value": "oPVyFC7WL2Lp",
"isInheritable": false,
"position": 90
},
{
"type": "relation",
"name": "internalLink",
"value": "YtSN43OrfzaA",
"isInheritable": false,
"position": 110
},
{
"type": "relation",
"name": "internalLink",
"value": "xYmIYSP6wE3F",
"isInheritable": false,
"position": 120
},
{
"type": "relation",
"name": "internalLink",
"value": "Ms1nauBra7gq",
"isInheritable": false,
"position": 130
},
{
"type": "relation",
"name": "internalLink",
"value": "eIg8jdvaoNNd",
"isInheritable": false,
"position": 140
},
{
"type": "relation",
"name": "internalLink",
"value": "3seOhtN8uLIY",
"isInheritable": false,
"position": 150
},
{
"type": "relation",
"name": "internalLink",
"value": "CdNpE2pqjmI6",
"isInheritable": false,
"position": 160
},
{
"type": "relation",
"name": "internalLink",
"value": "luNhaphA37EO",
"isInheritable": false,
"position": 170
} }
], ],
"format": "markdown", "format": "markdown",
@ -2845,6 +2845,20 @@
"isInheritable": false, "isInheritable": false,
"position": 30 "position": 30
}, },
{
"type": "relation",
"name": "internalLink",
"value": "RDslemsQ6gCp",
"isInheritable": false,
"position": 40
},
{
"type": "relation",
"name": "internalLink",
"value": "xYmIYSP6wE3F",
"isInheritable": false,
"position": 50
},
{ {
"type": "label", "type": "label",
"name": "iconClass", "name": "iconClass",
@ -2858,20 +2872,6 @@
"value": "tabs", "value": "tabs",
"isInheritable": false, "isInheritable": false,
"position": 40 "position": 40
},
{
"type": "relation",
"name": "internalLink",
"value": "RDslemsQ6gCp",
"isInheritable": false,
"position": 50
},
{
"type": "relation",
"name": "internalLink",
"value": "xYmIYSP6wE3F",
"isInheritable": false,
"position": 60
} }
], ],
"format": "markdown", "format": "markdown",
@ -2927,6 +2927,20 @@
"type": "text", "type": "text",
"mime": "text/html", "mime": "text/html",
"attributes": [ "attributes": [
{
"type": "relation",
"name": "internalLink",
"value": "x0JgW8UqGXvq",
"isInheritable": false,
"position": 10
},
{
"type": "relation",
"name": "internalLink",
"value": "x3i7MxGccDuM",
"isInheritable": false,
"position": 20
},
{ {
"type": "relation", "type": "relation",
"name": "internalLink", "name": "internalLink",
@ -2934,6 +2948,41 @@
"isInheritable": false, "isInheritable": false,
"position": 30 "position": 30
}, },
{
"type": "relation",
"name": "internalLink",
"value": "p9kXRFAkwN4o",
"isInheritable": false,
"position": 40
},
{
"type": "relation",
"name": "internalLink",
"value": "OR8WJ7Iz9K4U",
"isInheritable": false,
"position": 50
},
{
"type": "relation",
"name": "internalLink",
"value": "CdNpE2pqjmI6",
"isInheritable": false,
"position": 60
},
{
"type": "relation",
"name": "internalLink",
"value": "SynTBQiBsdYJ",
"isInheritable": false,
"position": 70
},
{
"type": "relation",
"name": "internalLink",
"value": "OFXdgB2nNk1F",
"isInheritable": false,
"position": 80
},
{ {
"type": "label", "type": "label",
"name": "iconClass", "name": "iconClass",
@ -2947,55 +2996,6 @@
"value": "launch-bar", "value": "launch-bar",
"isInheritable": false, "isInheritable": false,
"position": 90 "position": 90
},
{
"type": "relation",
"name": "internalLink",
"value": "OFXdgB2nNk1F",
"isInheritable": false,
"position": 100
},
{
"type": "relation",
"name": "internalLink",
"value": "SynTBQiBsdYJ",
"isInheritable": false,
"position": 110
},
{
"type": "relation",
"name": "internalLink",
"value": "CdNpE2pqjmI6",
"isInheritable": false,
"position": 120
},
{
"type": "relation",
"name": "internalLink",
"value": "OR8WJ7Iz9K4U",
"isInheritable": false,
"position": 130
},
{
"type": "relation",
"name": "internalLink",
"value": "p9kXRFAkwN4o",
"isInheritable": false,
"position": 140
},
{
"type": "relation",
"name": "internalLink",
"value": "x3i7MxGccDuM",
"isInheritable": false,
"position": 150
},
{
"type": "relation",
"name": "internalLink",
"value": "x0JgW8UqGXvq",
"isInheritable": false,
"position": 160
} }
], ],
"format": "markdown", "format": "markdown",
@ -5330,6 +5330,20 @@
"type": "text", "type": "text",
"mime": "text/markdown", "mime": "text/markdown",
"attributes": [ "attributes": [
{
"type": "relation",
"name": "internalLink",
"value": "xYmIYSP6wE3F",
"isInheritable": false,
"position": 10
},
{
"type": "relation",
"name": "internalLink",
"value": "x3i7MxGccDuM",
"isInheritable": false,
"position": 20
},
{ {
"type": "label", "type": "label",
"name": "shareAlias", "name": "shareAlias",
@ -5343,20 +5357,6 @@
"value": "bx bx-bookmarks", "value": "bx bx-bookmarks",
"isInheritable": false, "isInheritable": false,
"position": 30 "position": 30
},
{
"type": "relation",
"name": "internalLink",
"value": "xYmIYSP6wE3F",
"isInheritable": false,
"position": 40
},
{
"type": "relation",
"name": "internalLink",
"value": "x3i7MxGccDuM",
"isInheritable": false,
"position": 50
} }
], ],
"format": "markdown", "format": "markdown",
@ -6049,7 +6049,16 @@
], ],
"format": "markdown", "format": "markdown",
"dataFileName": "Icon Packs.md", "dataFileName": "Icon Packs.md",
"attachments": [] "attachments": [
{
"attachmentId": "CPieIjN3b77m",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "Icon Packs_image.png"
}
]
} }
] ]
}, },

View File

@ -1,12 +1,20 @@
# Icon Packs # Icon Packs
## Importing an existing icon pack <figure class="image image-style-align-right image_resized" style="width:45.14%;"><img style="aspect-ratio:854/649;" src="Icon Packs_image.png" width="854" height="649"></figure>
By default, Trilium comes with a set of icons called Boxicons v2. Since v0.102.0, custom icon packs allow a wider selection of icons for notes.
Icon packs are specific to Trilium, so they must either be created from scratch (see below) or imported from a ZIP file from a third-party developer. Icon packs are specific to Trilium, so they must either be created from scratch (see below) or imported from a ZIP file from a third-party developer.
## Sample icon packs
The Trilium team maintains a few icon packs that are not shipped with Trilium. These icon packs can be found on the official website on the [Resources page](https://triliumnotes.org/resources).
## Importing an existing icon pack
> [!NOTE] > [!NOTE]
> **Icon packs are third-party content** > **Icon packs are third-party content**
> >
> The Trilium maintainers are not responsible for keeping these icon packs up to date. If you have an issue with a specific icon pack, then the issue must be reported to the third-party developer responsible for it, not the Trilium team. > Apart from the [sample icon packs](https://triliumnotes.org/resources), the Trilium maintainers are not responsible for keeping icon packs up to date. If you have an issue with a specific icon pack, then the issue must be reported to the third-party developer responsible for it, not the Trilium team.
To import an icon pack: To import an icon pack:

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

View File

@ -57,11 +57,13 @@ Right click either the _Available launchers_ or _Visible launchers_ sections and
1. **Note Launcher** 1. **Note Launcher**
A note launcher will simply navigate to a specified note. A note launcher will simply navigate to a specified note.
1. Set the `target` promoted attribute to the note to navigate to. 1. Set the `target` promoted attribute to the note to navigate to.
2. Optionally, set `hoistedNote` to hoist a particular note. See <a class="reference-link" href="../Navigation/Note%20Hoisting.md">Note Hoisting</a> for more information. 2. Optionally, set `hoistedNote` to hoist a particular note. See <a class="reference-link" href="../Navigation/Note%20Hoisting.md">Note Hoisting</a> for more information.
3. Optionally, set a `keyboardShortcut` to trigger the launcher. 3. Optionally, set a `keyboardShortcut` to trigger the launcher.
2. **Script Launcher** 2. **Script Launcher**
An advanced launcher which will run a script upon pressing. See <a class="reference-link" href="../../Scripting.md">Scripting</a> for more information. An advanced launcher which will run a script upon pressing. See <a class="reference-link" href="../../Scripting.md">Scripting</a> for more information.
1. Set `script` to point to the desired script to run. 1. Set `script` to point to the desired script to run.
2. Optionally, set a `keyboardShortcut` to trigger the launcher. 2. Optionally, set a `keyboardShortcut` to trigger the launcher.
3. **Custom Widget** 3. **Custom Widget**