'use client' import React from 'react' import { useEditor, EditorContent } from '@tiptap/react' import StarterKit from '@tiptap/starter-kit' import learnhouseIcon from 'public/learnhouse_icon.png' import { ToolbarButtons } from './Toolbar/ToolbarButtons' import { motion } from 'framer-motion' import Image from 'next/image' import styled from 'styled-components' import { DividerVerticalIcon, SlashIcon } from '@radix-ui/react-icons' import learnhouseAI_icon from 'public/learnhouse_ai_simple.png' import { AIEditorStateTypes, useAIEditor, useAIEditorDispatch, } from '@components/Contexts/AI/AIEditorContext' // Extensions import InfoCallout from './Extensions/Callout/Info/InfoCallout' import WarningCallout from './Extensions/Callout/Warning/WarningCallout' import ImageBlock from './Extensions/Image/ImageBlock' import Youtube from '@tiptap/extension-youtube' import VideoBlock from './Extensions/Video/VideoBlock' import { Eye } from 'lucide-react' import MathEquationBlock from './Extensions/MathEquation/MathEquationBlock' import PDFBlock from './Extensions/PDF/PDFBlock' import QuizBlock from './Extensions/Quiz/QuizBlock' import ToolTip from '@components/StyledElements/Tooltip/Tooltip' import Link from 'next/link' import { getCourseThumbnailMediaDirectory } from '@services/media/media' // Lowlight import { common, createLowlight } from 'lowlight' const lowlight = createLowlight(common) import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight' import css from 'highlight.js/lib/languages/css' import js from 'highlight.js/lib/languages/javascript' import ts from 'highlight.js/lib/languages/typescript' import html from 'highlight.js/lib/languages/xml' import python from 'highlight.js/lib/languages/python' import java from 'highlight.js/lib/languages/java' import { CourseProvider } from '@components/Contexts/CourseContext' import { useLHSession } from '@components/Contexts/LHSessionContext' import AIEditorToolkit from './AI/AIEditorToolkit' import useGetAIFeatures from '@components/AI/Hooks/useGetAIFeatures' import Collaboration from '@tiptap/extension-collaboration' import CollaborationCursor from '@tiptap/extension-collaboration-cursor' import ActiveAvatars from './ActiveAvatars' import { getUriWithOrg } from '@services/config/config' interface Editor { content: string activity: any course: any org: any session: any ydoc: any hocuspocusProvider: any, isCollabEnabledOnThisOrg: boolean userRandomColor: string mouseMovements: any setContent: (content: string) => void } function Editor(props: Editor) { const session = useLHSession() as any const dispatchAIEditor = useAIEditorDispatch() as any const aiEditorState = useAIEditor() as AIEditorStateTypes const is_ai_feature_enabled = useGetAIFeatures({ feature: 'editor' }) const [isButtonAvailable, setIsButtonAvailable] = React.useState(false) React.useEffect(() => { if (is_ai_feature_enabled) { setIsButtonAvailable(true) } }, [is_ai_feature_enabled]) // remove course_ from course_uuid const course_uuid = props.course.course_uuid.substring(7) // remove activity_ from activity_uuid const activity_uuid = props.activity.activity_uuid.substring(9) // Code Block Languages for Lowlight lowlight.register('html', html) lowlight.register('css', css) lowlight.register('js', js) lowlight.register('ts', ts) lowlight.register('python', python) lowlight.register('java', java) const editor: any = useEditor({ editable: true, extensions: [ StarterKit.configure({ // The Collaboration extension comes with its own history handling history: props.isCollabEnabledOnThisOrg ? false : undefined, }), InfoCallout.configure({ editable: true, }), WarningCallout.configure({ editable: true, }), ImageBlock.configure({ editable: true, activity: props.activity, }), VideoBlock.configure({ editable: true, activity: props.activity, }), MathEquationBlock.configure({ editable: true, activity: props.activity, }), PDFBlock.configure({ editable: true, activity: props.activity, }), QuizBlock.configure({ editable: true, activity: props.activity, }), Youtube.configure({ controls: true, modestBranding: true, }), CodeBlockLowlight.configure({ lowlight, }), // Add Collaboration and CollaborationCursor only if isCollabEnabledOnThisOrg is true ...(props.isCollabEnabledOnThisOrg ? [ Collaboration.configure({ document: props.hocuspocusProvider?.document, }), CollaborationCursor.configure({ provider: props.hocuspocusProvider, user: { name: props.session.data.user.first_name + ' ' + props.session.data.user.last_name, color: props.userRandomColor, }, }), ] : []), ], // If collab is enabled the onSynced callback ensures initial content is set only once using editor.setContent(), preventing repetitive content insertion on editor syncs. content: props.isCollabEnabledOnThisOrg ? null : props.content, }) return ( {' '} {props.course.name} {props.activity.name}{' '}
{isButtonAvailable && (
dispatchAIEditor({ type: aiEditorState.isModalOpen ? 'setIsModalClose' : 'setIsModalOpen', }) } style={{ background: 'conic-gradient(from 32deg at 53.75% 50%, rgb(35, 40, 93) 4deg, rgba(20, 0, 52, 0.95) 59deg, rgba(164, 45, 238, 0.88) 281deg)', }} className="rounded-md px-3 py-2 drop-shadow-md flex items-center space-x-1.5 text-sm text-white hover:cursor-pointer transition delay-150 duration-300 ease-in-out hover:scale-105" > {' '} {' '} AI Editor
)}
props.setContent(editor.getJSON())} > {' '} Save{' '}
) } const Page = styled.div` height: 100vh; width: 100%; padding-top: 30px; // dots background background-image: radial-gradient(#4744446b 1px, transparent 1px), radial-gradient(#4744446b 1px, transparent 1px); background-position: 0 0, 25px 25px; background-size: 50px 50px; background-attachment: fixed; background-repeat: repeat; ` const EditorTop = styled.div` border-radius: 15px; margin: 40px; margin-top: 0px; margin-bottom: 20px; padding: 10px; display: flex; justify-content: space-between; box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.03); position: fixed; z-index: 303; width: -webkit-fill-available; ` // Inside EditorTop const EditorDocSection = styled.div` display: flex; flex-direction: column; ` const EditorUsersSection = styled.div` display: flex; flex-direction: row; justify-content: center; align-items: center; ` const EditorLeftOptionsSection = styled.div` display: flex; flex-direction: row; justify-content: center; align-items: center; ` // Inside EditorDocSection const EditorInfoWrapper = styled.div` display: flex; flex-direction: row; margin-bottom: 5px; ` const EditorButtonsWrapper = styled.div`` // Inside EditorUsersSection const EditorUserProfileWrapper = styled.div` padding-right: 8px; svg { border-radius: 7px; } ` // Inside EditorInfoWrapper //..todo const EditorInfoLearnHouseLogo = styled(Image)` border-radius: 6px; margin-right: 0px; ` const EditorInfoDocName = styled.div` font-size: 16px; justify-content: center; align-items: center; display: flex; margin-left: 10px; color: #494949; svg { margin-left: 4px; margin-right: 4px; padding: 3px; color: #353535; } ` const EditorInfoThumbnail = styled.img` height: 25px; width: 56px; object-fit: cover; object-position: top; border-radius: 7px; margin-left: 5px; &:hover { cursor: pointer; } ` export const EditorContentWrapper = styled.div` margin: 40px; margin-top: 90px; background-color: white; border-radius: 10px; z-index: 300; box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.03); // disable chrome outline .ProseMirror { h1 { font-size: 30px; font-weight: 600; margin-top: 10px; margin-bottom: 10px; } h2 { font-size: 25px; font-weight: 600; margin-top: 10px; margin-bottom: 10px; } h3 { font-size: 20px; font-weight: 600; margin-top: 10px; margin-bottom: 10px; } h4 { font-size: 18px; font-weight: 600; margin-top: 10px; margin-bottom: 10px; } h5 { font-size: 16px; font-weight: 600; margin-top: 10px; margin-bottom: 10px; } padding-left: 20px; padding-right: 20px; padding-bottom: 20px; padding-top: 20px; &:focus { outline: none !important; outline-style: none !important; box-shadow: none !important; } // Code Block pre { background: #0d0d0d; border-radius: 0.5rem; color: #fff; font-family: 'JetBrainsMono', monospace; padding: 0.75rem 1rem; code { background: none; color: inherit; font-size: 0.8rem; padding: 0; } .hljs-comment, .hljs-quote { color: #616161; } .hljs-variable, .hljs-template-variable, .hljs-attribute, .hljs-tag, .hljs-name, .hljs-regexp, .hljs-link, .hljs-name, .hljs-selector-id, .hljs-selector-class { color: #f98181; } .hljs-number, .hljs-meta, .hljs-built_in, .hljs-builtin-name, .hljs-literal, .hljs-type, .hljs-params { color: #fbbc88; } .hljs-string, .hljs-symbol, .hljs-bullet { color: #b9f18d; } .hljs-title, .hljs-section { color: #faf594; } .hljs-keyword, .hljs-selector-tag { color: #70cff8; } .hljs-emphasis { font-style: italic; } .hljs-strong { font-weight: 700; } } } iframe { border-radius: 6px; border: none; min-width: 200px; width: 100%; height: 440px; min-height: 200px; display: block; outline: 0px solid transparent; } ul, ol { padding: 0 1rem; padding-left: 20px; list-style-type: decimal; } ` export default Editor