Files
ttstd-infinite-canvas/web/src/lib/canvas/canvas-rich-text.ts
ddcat666 ea15000541 feat: 完成短剧项目空间与结构化资产创作闭环 (#38)
* feat(canvas): 短剧项目空间 - 建立项目章节与画布关联 MVP

* feat(canvas): 短剧项目空间 - 完善项目资产、镜头与流程闭环

Refs #30

* fix(deploy): 本地部署 - 修复前端构建并忽略本地 PostgreSQL 配置

* feat(*): 短剧项目空间 - 完成项目、章节、资产、角色与画布创作闭环

Refs #30

---------

Co-authored-by: ddCat <ddcat666@88.com>
2026-07-27 10:45:11 +08:00

48 lines
1.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { JSONContent } from "@tiptap/core";
import { generateHTML } from "@tiptap/core";
import CharacterCount from "@tiptap/extension-character-count";
import Color from "@tiptap/extension-color";
import Highlight from "@tiptap/extension-highlight";
import Placeholder from "@tiptap/extension-placeholder";
import TextAlign from "@tiptap/extension-text-align";
import { TextStyle } from "@tiptap/extension-text-style";
import StarterKit from "@tiptap/starter-kit";
export function createCanvasRichTextExtensions(placeholder?: string) {
return [
StarterKit.configure({ heading: { levels: [1, 2, 3] }, link: { openOnClick: false, autolink: true } }),
TextAlign.configure({ types: ["heading", "paragraph"] }),
TextStyle,
Color.configure({ types: ["textStyle"] }),
Highlight.configure({ multicolor: true }),
CharacterCount,
...(placeholder ? [Placeholder.configure({ placeholder })] : []),
];
}
export function canvasRichTextHTML(document?: Record<string, unknown>) {
if (!document) return "";
try {
// 仅接受 Tiptap 结构化 JSON,不启用原始 HTML;输出链接再按协议收口。
const template = window.document.createElement("template");
template.innerHTML = generateHTML(document as JSONContent, createCanvasRichTextExtensions());
template.content.querySelectorAll("a[href]").forEach((link) => {
if (!isSafeCanvasRichTextLink(link.getAttribute("href") || "")) link.removeAttribute("href");
});
return template.innerHTML;
} catch {
return "";
}
}
export function isSafeCanvasRichTextLink(href: string) {
const value = href.trim();
if (!value) return false;
try {
const url = new URL(value, window.location.origin);
return ["http:", "https:", "mailto:", "tel:"].includes(url.protocol);
} catch {
return false;
}
}