/** @jsx h */ /** @jsxFrag Fragment */ import { Fragment, h, render, useEffect, useState } from "./deps/preact.tsx"; import { useKaTeX } from "./useKaTeX.ts"; import type { KatexOptions } from "./deps/katex.ts"; import { version } from "./deps/katex.ts"; import { PopupContainer, style as popupStyle } from "./PopupContainer.tsx"; import { useCursorObserver } from "./useCursorObserver.ts"; const App = (props: KatexOptions) => { const { ref, error, setFormula } = useKaTeX("", props); // 数式rendering用hook const [open, setOpen] = useState(false); // popupの開閉 const [cursor, setCursor] = useState({ top: 0, left: 0, }); // cursorの位置 const positions = useCursorObserver(); // .formula内にcursorが来たらpreviewを開始する useEffect(() => { if (!positions) return; const { elements, cursor: { left }, editor, } = positions; const formulaDOM = elements.find((element) => ( element.tagName === "CODE" && element.nextElementSibling?.classList?.contains("preview") ) || element.matches(".formula.error") ); if (!formulaDOM) { setOpen(false); return; } setOpen(true); setFormula((formulaDOM.textContent ?? "").slice(3, -1)); // popupを出すy座標は、[$ ]の上端に合わせる const { top: formulaTop } = formulaDOM.getBoundingClientRect(); setCursor({ top: formulaTop - editor.top, left: left - editor.left, }); }, [positions]); return ( <> {error && {error}} ); }; export type MountOptions = { throwOnError?: boolean; }; export function mount(options?: MountOptions) { const { throwOnError = false } = options ?? {}; const app = document.createElement("div"); app.dataset.userscriptName = "katex-previewer"; document.getElementById("editor")!.append(app); const shadowRoot = app.attachShadow({ mode: "open" }); render(, shadowRoot); }