在 SolidJS 中展示 Typsttitle
2024-09-07date
description
先上效果:
主要使用了 typst.ts 项目,因为有React的example所以适配起Solidjs的起来也十分方便。
Notice
pr已经合并,现在可以直接安装使用
首先要用 typst-ts-cli
将typ文档编译成vector格式(后缀 artifact.sir.in)然后再加载。
安装cli:
cargo install --locked --git https://github.com/Myriad-Dreamin/typst.ts typst-ts-cli
编译一下,用法和typst的cli基本相同:
typst-ts-cli compile --entry ./example.typ --format vector
然后把产生的 example.artifact.sir.in
当静态资源放着,比如public
文件夹里。
Tips
截至24年9月,typst-ts-cli 不同版本似乎编译产物有很大差异,去年12月的release编译的vector数据不能被从主分支编译的wasm正常解析,建议版本对上号。
开始编写组件
因为有了react的前车之鉴所以这个非常简单。
然后下文的 local-font
permission query 只在 chromium 用得上,firefox似乎还没实现。
import { withGlobalRenderer } from "@myriaddreamin/typst.ts/dist/esm/contrib/global-renderer.mjs";
import * as typst from "@myriaddreamin/typst.ts";
import { createEffect, createSignal } from "solid-js";
export interface TypstDocumentProps {
fill?: string;
artifact?: Uint8Array;
format?: "vector";
}
let moduleInitOptions: typst.InitOptions = {
beforeBuild: [],
getModule: () =>
"_build/node_modules/@myriaddreamin/typst-ts-renderer/pkg/typst_ts_renderer_bg.wasm",
};
export const TypstDocument = ({
fill,
artifact,
format,
}: TypstDocumentProps) => {
/// --- beg: manipulate permission --- ///
// todo: acquire permission.
const [permission, setPermissionInternal] = createSignal(false);
const setPermissionAndOk = (status: PermissionStatus) => {
if (status.state === "granted") {
setPermissionInternal(true);
return true;
}
setPermissionInternal(false);
return false;
};
createEffect(() => {
/// only works in chromium
navigator.permissions.query({ name: 'local-fonts' as PermissionName }).then(status => {
if (setPermissionAndOk(status)) {
return false;
}
status.addEventListener('change', event => {
console.log(event, status);
setPermissionAndOk(status);
});
});
});
/// --- end: manipulate permission --- ///
/// --- beg: update document --- ///
const [displayDivRef, setDisplayDivRef] = createSignal<
HTMLDivElement | undefined
>();
createEffect(() => {
const doRender = (renderer: typst.TypstRenderer) => {
const divElem = displayDivRef();
if (!divElem) {
return;
}
return renderer.renderToCanvas({
artifactContent: artifact || new Uint8Array(0),
format: "vector",
backgroundColor: fill,
container: divElem,
pixelPerPt: 8,
});
};
/// get display layer div
const divElem = displayDivRef();
if (!divElem) {
return;
}
/// we allow empty artifact
if (!artifact?.length) {
divElem!.innerHTML = "";
return;
}
console.log(displayDivRef());
/// render after init
withGlobalRenderer(typst.createTypstRenderer, moduleInitOptions, doRender);
}, [permission, displayDivRef, fill, artifact, format]);
/// --- end: update document --- ///
return <div ref={setDisplayDivRef}></div>;
};
TypstDocument.setWasmModuleInitOptions = (opts: typst.InitOptions) => {
moduleInitOptions = opts;
};
import { createEffect, createResource, createSignal, onMount } from "solid-js";
import { TypstDocument } from "../lib/TypstDocument";
export default function Typstest() {
const getArtifactData = async () => {
const response = await fetch(
'http://localhost:3000/readme.artifact.sir.in',
).then(response => response.arrayBuffer());
return (new Uint8Array(response));
};
const [a] = createResource(getArtifactData);
return (
<div class="w-full h-full justify-center items-center flex flex-col">
This is for testing the [WIP] typst solidjs component
<TypstDocument fill="#343541" artifact={a()} />
</div>
);
}
最终看到的就是文章开头链接所指的景象了。