Add in-content video embed and image replace/delete controls

- KinescopeVideo TipTap node: insert video anywhere in lesson content via toolbar button; editor shows ID preview, student view renders KinescopePlayer
- ImageWithControls NodeView: hover over any image in editor to reveal Replace and Delete buttons

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-26 11:27:52 +05:00
parent 287347168d
commit 7e6ad8067c
2 changed files with 200 additions and 3 deletions
+41 -1
View File
@@ -1,16 +1,56 @@
"use client";
import { useEditor, EditorContent } from "@tiptap/react";
import { useEditor, EditorContent, NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react";
import { Node, mergeAttributes } from "@tiptap/core";
import StarterKit from "@tiptap/starter-kit";
import Image from "@tiptap/extension-image";
import Link from "@tiptap/extension-link";
import Underline from "@tiptap/extension-underline";
import { KinescopePlayer } from "@/components/player/kinescope-player";
function KinescopeVideoStudentView({ node }: { node: { attrs: Record<string, unknown> } }) {
const videoId = node.attrs.videoId as string;
if (!videoId) return null;
return (
<NodeViewWrapper>
<div className="my-6">
<KinescopePlayer videoId={videoId} />
</div>
</NodeViewWrapper>
);
}
const KinescopeVideoExtension = Node.create({
name: "kinescopeVideo",
group: "block",
atom: true,
addAttributes() {
return { videoId: { default: "" } };
},
parseHTML() {
return [{ tag: "div[data-kinescope-video]" }];
},
renderHTML({ HTMLAttributes }) {
return ["div", mergeAttributes({ "data-kinescope-video": "" }, HTMLAttributes)];
},
addNodeView() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return ReactNodeViewRenderer(KinescopeVideoStudentView as any);
},
});
export function LessonContent({ content }: { content: object }) {
const editor = useEditor({
extensions: [
StarterKit,
Underline,
Image.configure({ inline: false }),
Link.configure({ openOnClick: true }),
KinescopeVideoExtension,
],
content,
editable: false,