diff --git a/apps/web/app/components/workspace/rich-document-editor.tsx b/apps/web/app/components/workspace/rich-document-editor.tsx new file mode 100644 index 00000000000..33fe1632d05 --- /dev/null +++ b/apps/web/app/components/workspace/rich-document-editor.tsx @@ -0,0 +1,856 @@ +"use client"; + +import { useEditor, EditorContent } from "@tiptap/react"; +import { BubbleMenu } from "@tiptap/react/menus"; +import StarterKit from "@tiptap/starter-kit"; +import Image from "@tiptap/extension-image"; +import Link from "@tiptap/extension-link"; +import { Table } from "@tiptap/extension-table"; +import TableRow from "@tiptap/extension-table-row"; +import TableCell from "@tiptap/extension-table-cell"; +import TableHeader from "@tiptap/extension-table-header"; +import TaskList from "@tiptap/extension-task-list"; +import TaskItem from "@tiptap/extension-task-item"; +import Placeholder from "@tiptap/extension-placeholder"; +import Underline from "@tiptap/extension-underline"; +import TextAlign from "@tiptap/extension-text-align"; +import { TextStyle } from "@tiptap/extension-text-style"; +import Color from "@tiptap/extension-color"; +import Highlight from "@tiptap/extension-highlight"; +import Superscript from "@tiptap/extension-superscript"; +import Subscript from "@tiptap/extension-subscript"; +import CharacterCount from "@tiptap/extension-character-count"; +import { useState, useCallback, useEffect, useRef } from "react"; + +import { + ToolbarGroup, + ToolbarDivider, + ToolbarButton, + BubbleButton, +} from "./editor-toolbar-primitives"; + +// --- Types --- + +export type RichDocumentEditorProps = { + mode: "docx" | "txt"; + initialHtml: string; + filePath: string; + onSave?: () => void; + /** Compact mode for sidebar preview rendering */ + compact?: boolean; +}; + +// --- Helpers --- + +function isDocxFile(name: string): boolean { + const ext = name.split(".").pop()?.toLowerCase() ?? ""; + return ext === "docx" || ext === "doc"; +} + +function isTxtFile(name: string): boolean { + return name.split(".").pop()?.toLowerCase() === "txt"; +} + +export { isDocxFile, isTxtFile }; + +/** Convert plain text into simple HTML paragraphs for Tiptap */ +export function textToHtml(text: string): string { + if (!text.trim()) {return "
";} + return text + .split("\n") + .map((line) => `${line.replace(/&/g, "&").replace(//g, ">") || "
"}