chore(core): integrate icon_packs service

This commit is contained in:
Elian Doran 2026-01-07 11:45:40 +02:00
parent 22590596da
commit f05fda871c
No known key found for this signature in database
10 changed files with 34 additions and 28 deletions

View File

@ -7,7 +7,7 @@ import assetPath from "../services/asset_path.js";
import attributeService from "../services/attributes.js";
import config from "../services/config.js";
import { getCurrentLocale } from "../services/i18n.js";
import { generateCss, generateIconRegistry, getIconPacks, MIME_TO_EXTENSION_MAPPINGS } from "../services/icon_packs.js";
import { icon_packs as iconPackService } from "@triliumnext/core";
import log from "../services/log.js";
import optionService from "../services/options.js";
import protectedSessionService from "../services/protected_session.js";
@ -30,7 +30,7 @@ export function bootstrap(req: Request, res: Response) {
const theme = options.theme;
const themeNote = attributeService.getNoteWithLabel("appTheme", theme);
const nativeTitleBarVisible = options.nativeTitleBarVisible === "true";
const iconPacks = getIconPacks();
const iconPacks = iconPackService.getIconPacks();
const currentLocale = getCurrentLocale();
res.send({
@ -62,12 +62,12 @@ export function bootstrap(req: Request, res: Response) {
currentLocale,
isRtl: !!currentLocale.rtl,
iconPackCss: iconPacks
.map(p => generateCss(p, p.builtin
? `${assetPath}/fonts/${p.fontAttachmentId}.${MIME_TO_EXTENSION_MAPPINGS[p.fontMime]}`
.map(p => iconPackService.generateCss(p, p.builtin
? `${assetPath}/fonts/${p.fontAttachmentId}.${iconPackService.MIME_TO_EXTENSION_MAPPINGS[p.fontMime]}`
: `api/attachments/download/${p.fontAttachmentId}`))
.filter(Boolean)
.join("\n\n"),
iconRegistry: generateIconRegistry(iconPacks),
iconRegistry: iconPackService.generateIconRegistry(iconPacks),
TRILIUM_SAFE_MODE: !!process.env.TRILIUM_SAFE_MODE
});
}

View File

@ -9,7 +9,7 @@ import type BBranch from "../../../becca/entities/bbranch.js";
import type BNote from "../../../becca/entities/bnote.js";
import { getClientDir, getShareThemeAssetDir } from "../../../routes/assets";
import { getDefaultTemplatePath, readTemplate, renderNoteForExport } from "../../../share/content_renderer";
import { getIconPacks, MIME_TO_EXTENSION_MAPPINGS, ProcessedIconPack } from "../../icon_packs";
import { icon_packs as iconPackService } from "@triliumnext/core";
import log from "../../log";
import NoteMeta, { NoteMetaFile } from "../../meta/note_meta";
import { RESOURCE_DIR } from "../../resource_dir";
@ -31,7 +31,7 @@ export default class ShareThemeExportProvider extends ZipExportProvider {
private indexMeta: NoteMeta | null = null;
private searchIndex: Map<string, SearchIndexEntry> = new Map();
private rootMeta: NoteMeta | null = null;
private iconPacks: ProcessedIconPack[] = [];
private iconPacks: iconPackService.ProcessedIconPack[] = [];
prepareMeta(metaFile: NoteMetaFile): void {
const assets = [
@ -56,7 +56,7 @@ export default class ShareThemeExportProvider extends ZipExportProvider {
dataFileName: "index.html"
};
this.rootMeta = metaFile.files[0];
this.iconPacks = getIconPacks();
this.iconPacks = iconPackService.getIconPacks();
metaFile.files.push(this.indexMeta);
}
@ -165,7 +165,7 @@ export default class ShareThemeExportProvider extends ZipExportProvider {
// Inject the custom fonts.
for (const iconPack of this.iconPacks) {
const extension = MIME_TO_EXTENSION_MAPPINGS[iconPack.fontMime];
const extension = iconPackService.MIME_TO_EXTENSION_MAPPINGS[iconPack.fontMime];
let fontData: Uint8Array | undefined;
if (iconPack.builtin) {
fontData = readFileSync(join(getClientDir(), "fonts", `${iconPack.fontAttachmentId}.${extension}`));

View File

@ -343,10 +343,6 @@ export function processStringOrBuffer(data: string | Buffer | null) {
}
}
export function safeExtractMessageAndStackFromError(err: unknown): [errMessage: string, errStack: string | undefined] {
return (err instanceof Error) ? [err.message, err.stack] as const : ["Unknown Error", undefined] as const;
}
/**
* Normalizes URL by removing trailing slashes and fixing double slashes.
* Preserves the protocol (http://, https://) but removes trailing slashes from the rest.
@ -453,9 +449,14 @@ function slugify(text: string) {
.replace(/(^-|-$)+/g, ""); // trim dashes
}
/** @deprecated */
export const escapeHtml = coreUtils.escapeHtml;
/** @deprecated */
export const unescapeHtml = coreUtils.unescapeHtml;
/** @deprecated */
export const randomSecureToken = coreUtils.randomSecureToken;
/** @deprecated */
export const safeExtractMessageAndStackFromError = coreUtils.safeExtractMessageAndStackFromError;
export default {
compareVersions,

View File

@ -12,7 +12,6 @@ import BAttachment from '../becca/entities/battachment.js';
import type BBranch from "../becca/entities/bbranch.js";
import BNote from "../becca/entities/bnote.js";
import assetPath, { assetUrlFragment } from "../services/asset_path.js";
import { generateCss, getIconPacks, MIME_TO_EXTENSION_MAPPINGS, ProcessedIconPack } from "../services/icon_packs.js";
import log from "../services/log.js";
import options from "../services/options.js";
import utils, { getResourceDir, isDev, safeExtractMessageAndStackFromError } from "../services/utils.js";
@ -21,6 +20,7 @@ import SBranch from "./shaca/entities/sbranch.js";
import type SNote from "./shaca/entities/snote.js";
import shaca from "./shaca/shaca.js";
import shareRoot from "./share_root.js";
import { icon_packs as iconPackService } from "@triliumnext/core";
const shareAdjustedAssetPath = isDev ? assetPath : `../${assetPath}`;
const templateCache: Map<string, string> = new Map();
@ -69,7 +69,7 @@ function getSharedSubTreeRoot(note: SNote | BNote | undefined): Subroot {
return getSharedSubTreeRoot(parentBranch.getParentNote());
}
export function renderNoteForExport(note: BNote, parentBranch: BBranch, basePath: string, ancestors: string[], iconPacks: ProcessedIconPack[]) {
export function renderNoteForExport(note: BNote, parentBranch: BBranch, basePath: string, ancestors: string[], iconPacks: iconPackService.ProcessedIconPack[]) {
const subRoot: Subroot = {
branch: parentBranch,
note: parentBranch.getNote()
@ -95,7 +95,7 @@ export function renderNoteForExport(note: BNote, parentBranch: BBranch, basePath
faviconUrl: `${basePath}favicon.ico`,
ancestors,
isStatic: true,
iconPackCss: iconPacks.map(p => generateCss(p, `${basePath}assets/icon-pack-${p.prefix.toLowerCase()}.${MIME_TO_EXTENSION_MAPPINGS[p.fontMime]}`))
iconPackCss: iconPacks.map(p => iconPackService.generateCss(p, `${basePath}assets/icon-pack-${p.prefix.toLowerCase()}.${iconPackService.MIME_TO_EXTENSION_MAPPINGS[p.fontMime]}`))
.filter(Boolean)
.join("\n\n"),
iconPackSupportedPrefixes: iconPacks.map(p => p.prefix)
@ -136,7 +136,7 @@ export function renderNoteContent(note: SNote) {
const customLogoId = note.getRelation("shareLogo")?.value;
const logoUrl = customLogoId ? `api/images/${customLogoId}/image.png` : `../${assetUrlFragment}/images/icon-color.svg`;
const iconPacks = getIconPacks().filter(p => p.builtin || !!shaca.notes[p.manifestNoteId]);
const iconPacks = iconPackService.getIconPacks().filter(p => p.builtin || !!shaca.notes[p.manifestNoteId]);
return renderNoteContentInternal(note, {
subRoot,
@ -147,8 +147,8 @@ export function renderNoteContent(note: SNote) {
ancestors,
isStatic: false,
faviconUrl: note.hasRelation("shareFavicon") ? `api/notes/${note.getRelationValue("shareFavicon")}/download` : `../favicon.ico`,
iconPackCss: iconPacks.map(p => generateCss(p, p.builtin
? `/share/assets/fonts/${p.fontAttachmentId}.${MIME_TO_EXTENSION_MAPPINGS[p.fontMime]}`
iconPackCss: iconPacks.map(p => iconPackService.generateCss(p, p.builtin
? `/share/assets/fonts/${p.fontAttachmentId}.${iconPackService.MIME_TO_EXTENSION_MAPPINGS[p.fontMime]}`
: `/share/api/attachments/${p.fontAttachmentId}/download`
))
.filter(Boolean)

View File

@ -20,6 +20,7 @@ export { default as app_info } from "./services/app_info";
export { default as keyboard_actions } from "./services/keyboard_actions";
export { default as entity_changes } from "./services/entity_changes";
export { default as hidden_subtree } from "./services/hidden_subtree";
export * as icon_packs from "./services/icon_packs";
export { getContext, type ExecutionContext } from "./services/context";
export * as cls from "./services/context";
export * from "./errors";

View File

@ -3,9 +3,9 @@ import { IconRegistry } from "@triliumnext/commons";
import type BAttachment from "../becca/entities/battachment";
import type BNote from "../becca/entities/bnote";
import boxiconsManifest from "./icon_pack_boxicons-v2.json" with { type: "json" };
import log from "./log";
import { getLog } from "./log";
import search from "./search/services/search";
import { safeExtractMessageAndStackFromError } from "./utils";
import { safeExtractMessageAndStackFromError } from "./utils/index";
const PREFERRED_MIME_TYPE = [
"font/woff2",
@ -64,7 +64,7 @@ export function getIconPacks() {
if (!iconPack) return false;
if (iconPack.prefix === "bx" || usedPrefixes.has(iconPack.prefix)) {
log.info(`Skipping icon pack with duplicate prefix '${iconPack.prefix}': ${iconPack.title} (${iconPack.manifestNoteId})`);
getLog().info(`Skipping icon pack with duplicate prefix '${iconPack.prefix}': ${iconPack.title} (${iconPack.manifestNoteId})`);
return false;
}
usedPrefixes.add(iconPack.prefix);
@ -103,25 +103,25 @@ export function generateIconRegistry(iconPacks: ProcessedIconPack[]): IconRegist
export function processIconPack(iconPackNote: BNote): ProcessedIconPack | undefined {
const manifest = iconPackNote.getJsonContentSafely() as IconPackManifest;
if (!manifest) {
log.error(`Icon pack is missing JSON manifest (or has syntax errors): ${iconPackNote.title} (${iconPackNote.noteId})`);
getLog().error(`Icon pack is missing JSON manifest (or has syntax errors): ${iconPackNote.title} (${iconPackNote.noteId})`);
return;
}
const attachment = determineBestFontAttachment(iconPackNote);
if (!attachment || !attachment.attachmentId) {
log.error(`Icon pack is missing WOFF/WOFF2/TTF attachment: ${iconPackNote.title} (${iconPackNote.noteId})`);
getLog().error(`Icon pack is missing WOFF/WOFF2/TTF attachment: ${iconPackNote.title} (${iconPackNote.noteId})`);
return;
}
const prefix = iconPackNote.getLabelValue("iconPack");
if (!prefix) {
log.error(`Icon pack is missing 'iconPack' label defining its prefix: ${iconPackNote.title} (${iconPackNote.noteId})`);
getLog().error(`Icon pack is missing 'iconPack' label defining its prefix: ${iconPackNote.title} (${iconPackNote.noteId})`);
return;
}
// Ensure prefix is alphanumeric only, dashes and underscores.
if (!/^[a-zA-Z0-9-_]+$/.test(prefix)) {
log.error(`Icon pack has invalid 'iconPack' prefix (only alphanumeric characters, dashes and underscores are allowed): ${iconPackNote.title} (${iconPackNote.noteId})`);
getLog().error(`Icon pack has invalid 'iconPack' prefix (only alphanumeric characters, dashes and underscores are allowed): ${iconPackNote.title} (${iconPackNote.noteId})`);
return;
}
@ -185,7 +185,7 @@ export function generateCss({ manifest, fontMime, builtin, fontAttachmentId, pre
${iconDeclarations.join("\n")}
`;
} catch (e) {
log.error(safeExtractMessageAndStackFromError(e));
getLog().error(safeExtractMessageAndStackFromError(e));
return null;
}
}

View File

@ -5,7 +5,7 @@ export default {
console.warn("Ignore search ", note.title);
},
searchNotes(searchString: string, opts: {}): BNote[] {
searchNotes(searchString: string, opts?: {}): BNote[] {
console.warn("Ignore search", searchString);
return [];
}

View File

@ -126,3 +126,7 @@ export const unescapeHtml = unescape;
export function randomSecureToken(bytes = 32) {
return encodeBase64(getCrypto().randomBytes(32));
}
export function safeExtractMessageAndStackFromError(err: unknown): [errMessage: string, errStack: string | undefined] {
return (err instanceof Error) ? [err.message, err.stack] as const : ["Unknown Error", undefined] as const;
}