mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-24 22:09:08 +08:00
feat(editor): migrate typst mermaid to native (#14499)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Native/WASM Mermaid and Typst SVG preview rendering on desktop and mobile, plus cross-platform Preview plugin integrations. * **Improvements** * Centralized, sanitized rendering bridge with automatic Typst font-directory handling and configurable native renderer selection. * More consistent and robust error serialization and worker-backed preview flows for improved stability and performance. * **Tests** * Extensive unit and integration tests for preview rendering, font discovery, sanitization, and error serialization. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { dialogHandlers } from './dialog';
|
||||
import { dbEventsV1, dbHandlersV1, nbstoreHandlers } from './nbstore';
|
||||
import { previewHandlers } from './preview';
|
||||
import { provideExposed } from './provide';
|
||||
import { workspaceEvents, workspaceHandlers } from './workspace';
|
||||
|
||||
@@ -8,6 +9,7 @@ export const handlers = {
|
||||
nbstore: nbstoreHandlers,
|
||||
workspace: workspaceHandlers,
|
||||
dialog: dialogHandlers,
|
||||
preview: previewHandlers,
|
||||
};
|
||||
|
||||
export const events = {
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
import {
|
||||
type MermaidRenderRequest,
|
||||
type MermaidRenderResult,
|
||||
renderMermaidSvg,
|
||||
renderTypstSvg,
|
||||
type TypstRenderRequest,
|
||||
type TypstRenderResult,
|
||||
} from '@affine/native';
|
||||
|
||||
const TYPST_FONT_DIRS_ENV = 'AFFINE_TYPST_FONT_DIRS';
|
||||
|
||||
function parseTypstFontDirsFromEnv() {
|
||||
const value = process.env[TYPST_FONT_DIRS_ENV];
|
||||
if (!value) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return value
|
||||
.split(path.delimiter)
|
||||
.map(dir => dir.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function getTypstFontDirCandidates() {
|
||||
const resourcesPath = process.resourcesPath ?? '';
|
||||
|
||||
return [
|
||||
...parseTypstFontDirsFromEnv(),
|
||||
path.join(resourcesPath, 'fonts'),
|
||||
path.join(resourcesPath, 'js', 'fonts'),
|
||||
path.join(resourcesPath, 'app.asar.unpacked', 'fonts'),
|
||||
path.join(resourcesPath, 'app.asar.unpacked', 'js', 'fonts'),
|
||||
];
|
||||
}
|
||||
|
||||
function resolveTypstFontDirs() {
|
||||
return Array.from(
|
||||
new Set(getTypstFontDirCandidates().map(dir => path.resolve(dir)))
|
||||
).filter(dir => fs.statSync(dir, { throwIfNoEntry: false })?.isDirectory());
|
||||
}
|
||||
|
||||
function withTypstFontDirs(
|
||||
request: TypstRenderRequest,
|
||||
fontDirs: string[]
|
||||
): TypstRenderRequest {
|
||||
const nextOptions = request.options ? { ...request.options } : {};
|
||||
if (!nextOptions.fontDirs?.length) {
|
||||
nextOptions.fontDirs = fontDirs;
|
||||
}
|
||||
return { ...request, options: nextOptions };
|
||||
}
|
||||
|
||||
const typstFontDirs = resolveTypstFontDirs();
|
||||
|
||||
export const previewHandlers = {
|
||||
renderMermaidSvg: async (
|
||||
request: MermaidRenderRequest
|
||||
): Promise<MermaidRenderResult> => {
|
||||
return renderMermaidSvg(request);
|
||||
},
|
||||
renderTypstSvg: async (
|
||||
request: TypstRenderRequest
|
||||
): Promise<TypstRenderResult> => {
|
||||
return renderTypstSvg(withTypstFontDirs(request, typstFontDirs));
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user