93 lines
2.8 KiB
TypeScript
93 lines
2.8 KiB
TypeScript
import type { Editor } from "@tiptap/react"
|
|
import type { AiChatContext } from "@/lib/ai/chat-context"
|
|
import { TIPTAP_SYNTAX_GUIDE } from "@/lib/ai/tiptap-syntax-guide"
|
|
|
|
const MAX_CONTEXT_CHARS = 14_000
|
|
const MAX_JSON_CHARS = 8_000
|
|
|
|
export type DocsEditorSnapshot = {
|
|
documentPath: string
|
|
documentTitle: string
|
|
sourcePath?: string
|
|
plainText: string
|
|
selectionText: string
|
|
contentJson: Record<string, unknown>
|
|
contentJsonTruncated: string
|
|
}
|
|
|
|
export function snapshotDocsEditor(
|
|
editor: Editor,
|
|
meta: { path: string; title: string; sourcePath?: string }
|
|
): DocsEditorSnapshot {
|
|
const { from, to } = editor.state.selection
|
|
const plainText = editor.getText()
|
|
const selectionText =
|
|
from === to ? "" : editor.state.doc.textBetween(from, to, "\n", "\n")
|
|
const contentJson = editor.getJSON() as Record<string, unknown>
|
|
const jsonRaw = JSON.stringify(contentJson)
|
|
const contentJsonTruncated =
|
|
jsonRaw.length > MAX_JSON_CHARS
|
|
? jsonRaw.slice(0, MAX_JSON_CHARS) + "…"
|
|
: jsonRaw
|
|
|
|
return {
|
|
documentPath: meta.path,
|
|
documentTitle: meta.title,
|
|
sourcePath: meta.sourcePath,
|
|
plainText: truncate(plainText, MAX_CONTEXT_CHARS),
|
|
selectionText: truncate(selectionText, 4000),
|
|
contentJson,
|
|
contentJsonTruncated,
|
|
}
|
|
}
|
|
|
|
export function docsContextFromSnapshot(
|
|
snapshot: DocsEditorSnapshot,
|
|
temporary = true
|
|
): AiChatContext {
|
|
return {
|
|
app: "docs",
|
|
temporary,
|
|
drivePath: snapshot.documentPath,
|
|
documentTitle: snapshot.documentTitle,
|
|
sourcePath: snapshot.sourcePath,
|
|
documentExcerpt: snapshot.plainText,
|
|
selectionText: snapshot.selectionText || undefined,
|
|
documentJson: snapshot.contentJsonTruncated,
|
|
}
|
|
}
|
|
|
|
export function docsSystemPromptExtra(context: AiChatContext): string {
|
|
const lines = [
|
|
"Tu édites un document UltiDocs (TipTap). Tu peux lire le contenu ci-dessous et proposer des modifications.",
|
|
"Pour appliquer une modification côté éditeur, renvoie un bloc JSON fenced:",
|
|
'```ulti-docs-apply\n{ "action": "insert_text"|"replace_selection"|"append_paragraph"|"set_content", ... }\n```',
|
|
"",
|
|
TIPTAP_SYNTAX_GUIDE,
|
|
]
|
|
if (context.documentTitle) {
|
|
lines.push("", `Document: ${context.documentTitle}`)
|
|
}
|
|
if (context.drivePath) {
|
|
lines.push(`Chemin sidecar: ${context.drivePath}`)
|
|
}
|
|
if (context.sourcePath) {
|
|
lines.push(`Fichier source: ${context.sourcePath}`)
|
|
}
|
|
if (context.selectionText) {
|
|
lines.push("", "Sélection utilisateur:", context.selectionText)
|
|
}
|
|
if (context.documentExcerpt) {
|
|
lines.push("", "Contenu (texte):", context.documentExcerpt)
|
|
}
|
|
if (context.documentJson) {
|
|
lines.push("", "Contenu (JSON tronqué si long):", context.documentJson)
|
|
}
|
|
return lines.join("\n")
|
|
}
|
|
|
|
function truncate(value: string, max: number): string {
|
|
if (value.length <= max) return value
|
|
return value.slice(0, max) + "…"
|
|
}
|