|
1 | 1 | import { mergeAttributes, Node, } from "@tiptap/core"; |
| 2 | +import type { JSONContent, } from "@tiptap/core"; |
2 | 3 | import { ReactNodeViewRenderer, } from "@tiptap/react"; |
3 | 4 | import { generateWidget, } from "../../../../services/generate"; |
4 | 5 | import { WidgetView, } from "./WidgetView"; |
5 | 6 |
|
| 7 | +function escapeAttr(s: string,): string { |
| 8 | + return s |
| 9 | + .replace(/&/g, "&",) |
| 10 | + .replace(/"/g, """,) |
| 11 | + .replace(/</g, "<",) |
| 12 | + .replace(/>/g, ">",); |
| 13 | +} |
| 14 | + |
6 | 15 | export interface WidgetAttributes { |
7 | 16 | id: string; |
8 | 17 | /** JSON-stringified Spec from json-render, or empty string */ |
@@ -49,14 +58,28 @@ export const WidgetExtension = Node.create({ |
49 | 58 | atom: true, |
50 | 59 | draggable: true, |
51 | 60 |
|
| 61 | + renderMarkdown(node: JSONContent,) { |
| 62 | + const a = node.attrs || {}; |
| 63 | + const parts = ['<div data-widget=""',]; |
| 64 | + if (a.id) parts.push(` data-id="${escapeAttr(String(a.id,),)}"`,); |
| 65 | + if (a.spec) parts.push(` data-spec="${escapeAttr(String(a.spec,),)}"`,); |
| 66 | + if (a.prompt) parts.push(` data-prompt="${escapeAttr(String(a.prompt,),)}"`,); |
| 67 | + if (a.saved) parts.push(' data-saved="true"',); |
| 68 | + parts.push("></div>",); |
| 69 | + return parts.join("",) + "\n\n"; |
| 70 | + }, |
| 71 | + |
52 | 72 | addAttributes() { |
53 | 73 | return { |
54 | 74 | id: { default: null, }, |
55 | 75 | spec: { default: "", }, |
56 | 76 | prompt: { default: "", }, |
57 | | - saved: { default: false, }, |
58 | | - loading: { default: false, }, |
59 | | - error: { default: "", }, |
| 77 | + saved: { |
| 78 | + default: false, |
| 79 | + parseHTML: (el: HTMLElement,) => el.getAttribute("data-saved",) === "true", |
| 80 | + }, |
| 81 | + loading: { default: false, rendered: false, }, |
| 82 | + error: { default: "", rendered: false, }, |
60 | 83 | }; |
61 | 84 | }, |
62 | 85 |
|
|
0 commit comments