feat(icon_pack): inject the icon pack into the client

This commit is contained in:
Elian Doran 2025-12-26 18:36:36 +02:00
parent 5f1bdf7264
commit e346963e76
No known key found for this signature in database
3 changed files with 29 additions and 19 deletions

View File

@ -8,6 +8,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover" />
<link rel="manifest" crossorigin="use-credentials" href="manifest.webmanifest">
<title>Trilium Notes</title>
<style id="trilium-icon-packs">
<%- iconPackCss %>
</style>
<script src="<%= appPath %>/runtime.js" crossorigin type="module"></script>
</head>
<body

View File

@ -1,20 +1,19 @@
"use strict";
import type { Request, Response } from "express";
import sql from "../services/sql.js";
import packageJson from "../../package.json" with { type: "json" };
import type BNote from "../becca/entities/bnote.js";
import appPath from "../services/app_path.js";
import assetPath from "../services/asset_path.js";
import attributeService from "../services/attributes.js";
import config from "../services/config.js";
import optionService from "../services/options.js";
import log from "../services/log.js";
import { isDev, isElectron, isWindows11 } from "../services/utils.js";
import protectedSessionService from "../services/protected_session.js";
import packageJson from "../../package.json" with { type: "json" };
import assetPath from "../services/asset_path.js";
import appPath from "../services/app_path.js";
import { generateToken as generateCsrfToken } from "./csrf_protection.js";
import type { Request, Response } from "express";
import type BNote from "../becca/entities/bnote.js";
import { getCurrentLocale } from "../services/i18n.js";
import { generateCss, getIconPacks } from "../services/icon_packs.js";
import log from "../services/log.js";
import optionService from "../services/options.js";
import protectedSessionService from "../services/protected_session.js";
import sql from "../services/sql.js";
import { isDev, isElectron, isWindows11 } from "../services/utils.js";
import { generateToken as generateCsrfToken } from "./csrf_protection.js";
type View = "desktop" | "mobile" | "print";
@ -35,10 +34,11 @@ function index(req: Request, res: Response) {
const theme = options.theme;
const themeNote = attributeService.getNoteWithLabel("appTheme", theme);
const nativeTitleBarVisible = options.nativeTitleBarVisible === "true";
const iconPacks = getIconPacks();
res.render(view, {
device: view,
csrfToken: csrfToken,
csrfToken,
themeCssUrl: getThemeCssUrl(theme, themeNote),
themeUseNextAsBase: themeNote?.getAttributeValue("label", "appThemeBase"),
headingStyle: options.headingStyle,
@ -61,7 +61,8 @@ function index(req: Request, res: Response) {
assetPath,
appPath,
baseApiUrl: 'api/',
currentLocale: getCurrentLocale()
currentLocale: getCurrentLocale(),
iconPackCss: iconPacks.map(p => generateCss(p)).join("\n\n")
});
}
@ -118,10 +119,9 @@ function getThemeCssUrl(theme: string, themeNote: BNote | null) {
return `${assetPath}/stylesheets/theme-next-dark.css`;
} else if (!process.env.TRILIUM_SAFE_MODE && themeNote) {
return `api/notes/download/${themeNote.noteId}`;
} else {
// baseline light theme
return false;
}
// baseline light theme
return false;
}
function getAppCssNoteIds() {

View File

@ -1,6 +1,7 @@
import type BAttachment from "../becca/entities/battachment";
import type BNote from "../becca/entities/bnote";
import log from "./log";
import search from "./search/services/search";
const PREFERRED_MIME_TYPE = [
"font/woff2",
@ -26,6 +27,12 @@ interface ProcessResult {
fontAttachmentId: string;
}
export function getIconPacks() {
return search.searchNotes("#iconPack")
.map(iconPackNote => processIconPack(iconPackNote))
.filter(Boolean) as ProcessResult[];
}
export function processIconPack(iconPackNote: BNote): ProcessResult | undefined {
const manifest = iconPackNote.getJsonContentSafely() as IconPackManifest;
if (!manifest) {
@ -65,7 +72,7 @@ export function determineBestFontAttachment(iconPackNote: BNote) {
export function generateCss({ manifest, fontAttachmentId, fontMime }: ProcessResult) {
const iconDeclarations: string[] = [];
for (const [ key, mapping ] of Object.entries(manifest.icons)) {
iconDeclarations.push(`.${manifest.prefix}.${key}::before { content: '\\${mapping.charCodeAt(0).toString(16)}' }`);
iconDeclarations.push(`.${manifest.prefix}.${key}::before { content: '\\${mapping.charCodeAt(0).toString(16)}'; }`);
}
return `\