mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-12 01:02:39 +02:00
feat: add support for HTML content in HitlEditPanel and PlateEditor components for only Confluence HITL tool
This commit is contained in:
parent
2bc6a0c3bc
commit
a9683bb1dc
8 changed files with 131 additions and 18 deletions
|
|
@ -1,7 +1,8 @@
|
|||
"use client";
|
||||
|
||||
import { MarkdownPlugin, remarkMdx } from "@platejs/markdown";
|
||||
import type { AnyPluginConfig } from "platejs";
|
||||
import { slateToHtml } from "@slate-serializers/html";
|
||||
import type { AnyPluginConfig, Descendant, Value } from "platejs";
|
||||
import { createPlatePlugin, Key, Plate, usePlateEditor } from "platejs/react";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import remarkGfm from "remark-gfm";
|
||||
|
|
@ -14,8 +15,12 @@ import { Editor, EditorContainer } from "@/components/ui/editor";
|
|||
export interface PlateEditorProps {
|
||||
/** Markdown string to load as initial content */
|
||||
markdown?: string;
|
||||
/** HTML string to load as initial content. Takes precedence over `markdown`. */
|
||||
html?: string;
|
||||
/** Called when the editor content changes, with serialized markdown */
|
||||
onMarkdownChange?: (markdown: string) => void;
|
||||
/** Called when the editor content changes, with serialized HTML. Use with the `html` prop. */
|
||||
onHtmlChange?: (html: string) => void;
|
||||
/**
|
||||
* Force permanent read-only mode (e.g. public/shared view).
|
||||
* When true, the editor cannot be toggled to editing mode.
|
||||
|
|
@ -57,7 +62,9 @@ export interface PlateEditorProps {
|
|||
|
||||
export function PlateEditor({
|
||||
markdown,
|
||||
html,
|
||||
onMarkdownChange,
|
||||
onHtmlChange,
|
||||
readOnly = false,
|
||||
placeholder = "Type...",
|
||||
variant = "default",
|
||||
|
|
@ -71,6 +78,7 @@ export function PlateEditor({
|
|||
extraPlugins = [],
|
||||
}: PlateEditorProps) {
|
||||
const lastMarkdownRef = useRef(markdown);
|
||||
const lastHtmlRef = useRef(html);
|
||||
|
||||
// Keep a stable ref to the latest onSave callback so the plugin shortcut
|
||||
// always calls the most recent version without re-creating the editor.
|
||||
|
|
@ -118,17 +126,28 @@ export function PlateEditor({
|
|||
},
|
||||
}),
|
||||
],
|
||||
// Use markdown deserialization for initial value if provided
|
||||
value: markdown
|
||||
? (editor) =>
|
||||
editor.getApi(MarkdownPlugin).markdown.deserialize(escapeMdxExpressions(markdown))
|
||||
: undefined,
|
||||
value: html
|
||||
? (editor) => editor.api.html.deserialize({ element: html }) as Value
|
||||
: markdown
|
||||
? (editor) =>
|
||||
editor.getApi(MarkdownPlugin).markdown.deserialize(escapeMdxExpressions(markdown))
|
||||
: undefined,
|
||||
});
|
||||
|
||||
// Update editor content when html prop changes externally
|
||||
useEffect(() => {
|
||||
if (html !== undefined && html !== lastHtmlRef.current) {
|
||||
lastHtmlRef.current = html;
|
||||
const newValue = editor.api.html.deserialize({ element: html });
|
||||
editor.tf.reset();
|
||||
editor.tf.setValue(newValue);
|
||||
}
|
||||
}, [html, editor]);
|
||||
|
||||
// Update editor content when markdown prop changes externally
|
||||
// (e.g., version switching in report panel)
|
||||
useEffect(() => {
|
||||
if (markdown !== undefined && markdown !== lastMarkdownRef.current) {
|
||||
if (!html && markdown !== undefined && markdown !== lastMarkdownRef.current) {
|
||||
lastMarkdownRef.current = markdown;
|
||||
const newValue = editor
|
||||
.getApi(MarkdownPlugin)
|
||||
|
|
@ -136,7 +155,7 @@ export function PlateEditor({
|
|||
editor.tf.reset();
|
||||
editor.tf.setValue(newValue);
|
||||
}
|
||||
}, [markdown, editor]);
|
||||
}, [html, markdown, editor]);
|
||||
|
||||
// When not forced read-only, the user can toggle between editing/viewing.
|
||||
const canToggleMode = !readOnly;
|
||||
|
|
@ -157,7 +176,10 @@ export function PlateEditor({
|
|||
// (initialized to true via usePlateEditor, toggled via ModeToolbarButton).
|
||||
{...(readOnly ? { readOnly: true } : {})}
|
||||
onChange={({ value }) => {
|
||||
if (onMarkdownChange) {
|
||||
if (onHtmlChange && html) {
|
||||
const serialized = slateToHtml(value as Descendant[]);
|
||||
onHtmlChange(serialized);
|
||||
} else if (onMarkdownChange) {
|
||||
const md = editor.getApi(MarkdownPlugin).markdown.serialize({ value });
|
||||
lastMarkdownRef.current = md;
|
||||
onMarkdownChange(md);
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@ function DateTimePickerField({
|
|||
export function HitlEditPanelContent({
|
||||
title: initialTitle,
|
||||
content: initialContent,
|
||||
contentFormat,
|
||||
extraFields,
|
||||
onSave,
|
||||
onClose,
|
||||
|
|
@ -202,13 +203,14 @@ export function HitlEditPanelContent({
|
|||
title: string;
|
||||
content: string;
|
||||
toolName: string;
|
||||
contentFormat?: "markdown" | "html";
|
||||
extraFields?: ExtraField[];
|
||||
onSave: (title: string, content: string, extraFieldValues?: Record<string, string>) => void;
|
||||
onClose?: () => void;
|
||||
showCloseButton?: boolean;
|
||||
}) {
|
||||
const [editedTitle, setEditedTitle] = useState(initialTitle);
|
||||
const markdownRef = useRef(initialContent);
|
||||
const contentRef = useRef(initialContent);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [extraFieldValues, setExtraFieldValues] = useState<Record<string, string>>(() => {
|
||||
if (!extraFields) return {};
|
||||
|
|
@ -219,8 +221,8 @@ export function HitlEditPanelContent({
|
|||
return initial;
|
||||
});
|
||||
|
||||
const handleMarkdownChange = useCallback((md: string) => {
|
||||
markdownRef.current = md;
|
||||
const handleContentChange = useCallback((content: string) => {
|
||||
contentRef.current = content;
|
||||
}, []);
|
||||
|
||||
const handleExtraFieldChange = useCallback((key: string, value: string) => {
|
||||
|
|
@ -231,7 +233,7 @@ export function HitlEditPanelContent({
|
|||
if (!editedTitle.trim()) return;
|
||||
setIsSaving(true);
|
||||
const extras = extraFields && extraFields.length > 0 ? extraFieldValues : undefined;
|
||||
onSave(editedTitle, markdownRef.current, extras);
|
||||
onSave(editedTitle, contentRef.current, extras);
|
||||
onClose?.();
|
||||
}, [editedTitle, onSave, onClose, extraFields, extraFieldValues]);
|
||||
|
||||
|
|
@ -299,8 +301,9 @@ export function HitlEditPanelContent({
|
|||
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<PlateEditor
|
||||
markdown={initialContent}
|
||||
onMarkdownChange={handleMarkdownChange}
|
||||
{...(contentFormat === "html"
|
||||
? { html: initialContent, onHtmlChange: handleContentChange }
|
||||
: { markdown: initialContent, onMarkdownChange: handleContentChange })}
|
||||
readOnly={false}
|
||||
preset="full"
|
||||
placeholder="Start writing..."
|
||||
|
|
@ -328,6 +331,7 @@ function DesktopHitlEditPanel() {
|
|||
title={panelState.title}
|
||||
content={panelState.content}
|
||||
toolName={panelState.toolName}
|
||||
contentFormat={panelState.contentFormat}
|
||||
extraFields={panelState.extraFields}
|
||||
onSave={panelState.onSave}
|
||||
onClose={closePanel}
|
||||
|
|
@ -361,6 +365,7 @@ function MobileHitlEditDrawer() {
|
|||
title={panelState.title}
|
||||
content={panelState.content}
|
||||
toolName={panelState.toolName}
|
||||
contentFormat={panelState.contentFormat}
|
||||
extraFields={panelState.extraFields}
|
||||
onSave={panelState.onSave}
|
||||
onClose={closePanel}
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@ export function RightPanel({ documentsPanel }: RightPanelProps) {
|
|||
title={hitlEditState.title}
|
||||
content={hitlEditState.content}
|
||||
toolName={hitlEditState.toolName}
|
||||
contentFormat={hitlEditState.contentFormat}
|
||||
extraFields={hitlEditState.extraFields}
|
||||
onSave={hitlEditState.onSave}
|
||||
onClose={closeHitlEdit}
|
||||
|
|
|
|||
|
|
@ -237,6 +237,7 @@ function ApprovalCard({
|
|||
title: pendingEdits?.title ?? args.title ?? "",
|
||||
content: pendingEdits?.content ?? args.content ?? "",
|
||||
toolName: "Confluence Page",
|
||||
contentFormat: "html",
|
||||
onSave: (newTitle, newContent) => {
|
||||
setIsPanelOpen(false);
|
||||
setPendingEdits({ title: newTitle, content: newContent });
|
||||
|
|
@ -334,7 +335,7 @@ function ApprovalCard({
|
|||
}}
|
||||
>
|
||||
<PlateEditor
|
||||
markdown={pendingEdits?.content ?? args.content ?? ""}
|
||||
html={pendingEdits?.content ?? args.content ?? ""}
|
||||
readOnly
|
||||
preset="readonly"
|
||||
editorVariant="none"
|
||||
|
|
|
|||
|
|
@ -254,6 +254,7 @@ function ApprovalCard({
|
|||
title: editedArgs.title,
|
||||
content: editedArgs.content,
|
||||
toolName: "Confluence Page",
|
||||
contentFormat: "html",
|
||||
onSave: (newTitle, newContent) => {
|
||||
setIsPanelOpen(false);
|
||||
setEditedArgs({
|
||||
|
|
@ -305,7 +306,7 @@ function ApprovalCard({
|
|||
}}
|
||||
>
|
||||
<PlateEditor
|
||||
markdown={page.body}
|
||||
html={page.body}
|
||||
readOnly
|
||||
preset="readonly"
|
||||
editorVariant="none"
|
||||
|
|
@ -348,7 +349,7 @@ function ApprovalCard({
|
|||
}}
|
||||
>
|
||||
<PlateEditor
|
||||
markdown={String(
|
||||
html={String(
|
||||
hasPanelEdits
|
||||
? editedArgs.content
|
||||
: (actionArgs.new_content ?? args.new_content)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue