Files
portfolio/lib/tiptap/fontFamily.ts
2026-01-12 14:36:10 +00:00

68 lines
1.8 KiB
TypeScript

import { Extension } from "@tiptap/core";
const allowedFonts = [
"Inter",
"ui-sans-serif",
"ui-serif",
"ui-monospace",
] as const;
export type AllowedFontFamily = (typeof allowedFonts)[number];
declare module "@tiptap/core" {
interface Commands<ReturnType> {
fontFamily: {
setFontFamily: (fontFamily: string) => ReturnType;
unsetFontFamily: () => ReturnType;
};
}
}
export const FontFamily = Extension.create({
name: "fontFamily",
addGlobalAttributes() {
return [
{
types: ["textStyle"],
attributes: {
fontFamily: {
default: null,
parseHTML: (element) => {
const raw = (element as HTMLElement).style.fontFamily;
if (!raw) return null;
// Normalize: remove quotes and take first family only
const first = raw.split(",")[0]?.trim().replace(/^["']|["']$/g, "");
if (!first) return null;
return first;
},
renderHTML: (attributes) => {
const fontFamily = attributes.fontFamily as string | null;
if (!fontFamily) return {};
if (!allowedFonts.includes(fontFamily as AllowedFontFamily)) return {};
return { style: `font-family: ${fontFamily}` };
},
},
},
},
];
},
addCommands() {
return {
setFontFamily:
(fontFamily: string) =>
({ chain }) => {
if (!allowedFonts.includes(fontFamily as AllowedFontFamily)) return false;
return chain().setMark("textStyle", { fontFamily }).run();
},
unsetFontFamily:
() =>
({ chain }) => {
return chain().setMark("textStyle", { fontFamily: null }).removeEmptyTextStyle().run();
},
};
},
});