73 lines
2.4 KiB
TypeScript
73 lines
2.4 KiB
TypeScript
import type { Editor } from "@tiptap/react"
|
|
import { ensureMinimalTipTapDoc } from "@/lib/drive/richtext-content"
|
|
|
|
export type DocsApplyAction =
|
|
| { action: "insert_text"; text: string }
|
|
| { action: "replace_selection"; text: string }
|
|
| { action: "append_paragraph"; text: string }
|
|
| { action: "set_content"; document: Record<string, unknown> }
|
|
|
|
export function parseDocsApplyPayload(raw: unknown): DocsApplyAction | null {
|
|
if (!raw || typeof raw !== "object") return null
|
|
const obj = raw as Record<string, unknown>
|
|
const action = obj.action
|
|
if (action === "insert_text" && typeof obj.text === "string") {
|
|
return { action, text: obj.text }
|
|
}
|
|
if (action === "replace_selection" && typeof obj.text === "string") {
|
|
return { action, text: obj.text }
|
|
}
|
|
if (action === "append_paragraph" && typeof obj.text === "string") {
|
|
return { action, text: obj.text }
|
|
}
|
|
if (action === "set_content" && obj.document && typeof obj.document === "object") {
|
|
return {
|
|
action,
|
|
document: obj.document as Record<string, unknown>,
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
/** Extrait un bloc ```ulti-docs-apply depuis une réponse assistant. */
|
|
export function extractDocsApplyFromMarkdown(text: string): DocsApplyAction | null {
|
|
const match = text.match(/```ulti-docs-apply\s*([\s\S]*?)```/i)
|
|
if (!match?.[1]) return null
|
|
try {
|
|
return parseDocsApplyPayload(JSON.parse(match[1].trim()))
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export function applyDocsAction(editor: Editor, cmd: DocsApplyAction): boolean {
|
|
if (!editor.isEditable) return false
|
|
switch (cmd.action) {
|
|
case "insert_text":
|
|
return editor.chain().focus().insertContent(cmd.text).run()
|
|
case "replace_selection":
|
|
if (editor.state.selection.empty) {
|
|
return editor.chain().focus().insertContent(cmd.text).run()
|
|
}
|
|
return editor.chain().focus().deleteSelection().insertContent(cmd.text).run()
|
|
case "append_paragraph": {
|
|
const blocks = cmd.text
|
|
.split(/\n{2,}/)
|
|
.map((p) => p.trim())
|
|
.filter(Boolean)
|
|
.map((p) => ({
|
|
type: "paragraph",
|
|
content: [{ type: "text", text: p }],
|
|
}))
|
|
if (blocks.length === 0) return false
|
|
return editor.chain().focus().insertContentAt(editor.state.doc.content.size, blocks).run()
|
|
}
|
|
case "set_content": {
|
|
const doc = ensureMinimalTipTapDoc(cmd.document)
|
|
return editor.commands.setContent(doc)
|
|
}
|
|
default:
|
|
return false
|
|
}
|
|
}
|