diff --git a/apps/client/src/ee/template/pages/template-editor.tsx b/apps/client/src/ee/template/pages/template-editor.tsx index 439cbb964..cef891060 100644 --- a/apps/client/src/ee/template/pages/template-editor.tsx +++ b/apps/client/src/ee/template/pages/template-editor.tsx @@ -32,6 +32,12 @@ import { } from "../queries/template-query"; import { useGetSpacesQuery } from "@/features/space/queries/space-query"; import useUserRole from "@/hooks/use-user-role"; +import { useAtomValue } from "jotai"; +import { userAtom } from "@/features/user/atoms/current-user-atom"; +import { FixedToolbar } from "@/features/editor/components/fixed-toolbar/fixed-toolbar"; +import { EditorLinkMenu } from "@/features/editor/components/link/link-menu"; +import { EditorBubbleMenu } from "@/features/editor/components/bubble-menu/bubble-menu"; +import { EditorAiMenu } from "@/ee/ai/components/editor/ai-menu/ai-menu"; import classes from "./template-editor.module.css"; @@ -39,6 +45,9 @@ export default function TemplateEditor() { const { t } = useTranslation(); const { templateId } = useParams<{ templateId: string }>(); const { isAdmin: isWorkspaceAdmin } = useUserRole(); + const user = useAtomValue(userAtom); + const editorToolbarEnabled = + user?.settings?.preferences?.editorToolbar ?? false; const { data: existingTemplate } = useGetTemplateByIdQuery(templateId || ""); const { data: spaces } = useGetSpacesQuery({ limit: 100 }); @@ -238,6 +247,10 @@ export default function TemplateEditor() { + {editorToolbarEnabled && editor && ( + + )} +
@@ -379,6 +392,13 @@ export default function TemplateEditor() { )}
+ {editor && ( + <> + + + + + )}
diff --git a/apps/client/src/features/editor/components/bubble-menu/bubble-menu.tsx b/apps/client/src/features/editor/components/bubble-menu/bubble-menu.tsx index 27c4b3854..5ad966af9 100644 --- a/apps/client/src/features/editor/components/bubble-menu/bubble-menu.tsx +++ b/apps/client/src/features/editor/components/bubble-menu/bubble-menu.tsx @@ -38,9 +38,11 @@ export interface BubbleMenuItem { type EditorBubbleMenuProps = Omit & { editor: Editor | null; + templateMode?: boolean; }; export const EditorBubbleMenu: FC = (props) => { + const { templateMode = false } = props; const { t } = useTranslation(); const [showAiMenu, setShowAiMenu] = useAtom(showAiMenuAtom); const [showCommentPopup, setShowCommentPopup] = useAtom(showCommentPopupAtom); @@ -232,8 +234,6 @@ export const EditorBubbleMenu: FC = (props) => { ))} - - = (props) => { )} - - - - - + + + {!templateMode && ( + + + + + + )}
); diff --git a/apps/client/src/features/editor/components/fixed-toolbar/fixed-toolbar.tsx b/apps/client/src/features/editor/components/fixed-toolbar/fixed-toolbar.tsx index d72db0c7d..b425753ee 100644 --- a/apps/client/src/features/editor/components/fixed-toolbar/fixed-toolbar.tsx +++ b/apps/client/src/features/editor/components/fixed-toolbar/fixed-toolbar.tsx @@ -1,12 +1,12 @@ import { FC } from "react"; import { useAtomValue } from "jotai"; +import type { Editor } from "@tiptap/react"; import { pageEditorAtom } from "@/features/editor/atoms/editor-atoms"; import { useToolbarState } from "./use-toolbar-state"; import { BlockTypeGroup } from "./groups/block-type-group"; import { InlineMarksGroup } from "./groups/inline-marks-group"; import { ColorGroup } from "./groups/color-group"; import { ListsGroup } from "./groups/lists-group"; -import { LinkGroup } from "./groups/link-group"; import { AlignmentGroup } from "./groups/alignment-group"; import { MediaGroup } from "./groups/media-group"; import { QuickInsertsGroup } from "./groups/quick-inserts-group"; @@ -16,8 +16,17 @@ import { AskAiGroup } from "./groups/ask-ai-group"; import { workspaceAtom } from "@/features/user/atoms/current-user-atom"; import classes from "./fixed-toolbar.module.css"; -export const FixedToolbar: FC = () => { - const editor = useAtomValue(pageEditorAtom); +type FixedToolbarProps = { + editor?: Editor | null; + templateMode?: boolean; +}; + +export const FixedToolbar: FC = ({ + editor: editorProp, + templateMode = false, +}) => { + const editorFromAtom = useAtomValue(pageEditorAtom); + const editor = editorProp ?? editorFromAtom; const state = useToolbarState(editor); const workspace = useAtomValue(workspaceAtom); const isGenerativeAiEnabled = workspace?.settings?.ai?.generative === true; @@ -48,14 +57,12 @@ export const FixedToolbar: FC = () => {
- -
- +
- +
diff --git a/apps/client/src/features/editor/components/fixed-toolbar/groups/link-group.tsx b/apps/client/src/features/editor/components/fixed-toolbar/groups/link-group.tsx deleted file mode 100644 index 334765928..000000000 --- a/apps/client/src/features/editor/components/fixed-toolbar/groups/link-group.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { FC } from "react"; -import { LinkSelector } from "@/features/editor/components/bubble-menu/link-selector"; - -export const LinkGroup: FC = () => { - return ; -}; diff --git a/apps/client/src/features/editor/components/fixed-toolbar/groups/media-group.tsx b/apps/client/src/features/editor/components/fixed-toolbar/groups/media-group.tsx index 7740204e1..5d99aa079 100644 --- a/apps/client/src/features/editor/components/fixed-toolbar/groups/media-group.tsx +++ b/apps/client/src/features/editor/components/fixed-toolbar/groups/media-group.tsx @@ -17,6 +17,7 @@ import { uploadPdfAction } from "@/features/editor/components/pdf/upload-pdf-act interface Props { editor: Editor; + templateMode?: boolean; } type UploadFn = ( @@ -60,7 +61,7 @@ function pickFile( input.click(); } -export const MediaGroup: FC = ({ editor }) => { +export const MediaGroup: FC = ({ editor, templateMode }) => { const { t } = useTranslation(); return ( @@ -78,24 +79,30 @@ export const MediaGroup: FC = ({ editor }) => { - } - onClick={() => pickFile(editor, "image/*", true, uploadImageAction)} - > - {t("Image")} - - } - onClick={() => pickFile(editor, "video/*", true, uploadVideoAction)} - > - {t("Video")} - - } - onClick={() => pickFile(editor, "audio/*", true, uploadAudioAction)} - > - {t("Audio")} - + {!templateMode && ( + } + onClick={() => pickFile(editor, "image/*", true, uploadImageAction)} + > + {t("Image")} + + )} + {!templateMode && ( + } + onClick={() => pickFile(editor, "video/*", true, uploadVideoAction)} + > + {t("Video")} + + )} + {!templateMode && ( + } + onClick={() => pickFile(editor, "audio/*", true, uploadAudioAction)} + > + {t("Audio")} + + )} } onClick={() => @@ -104,14 +111,16 @@ export const MediaGroup: FC = ({ editor }) => { > PDF - } - onClick={() => - pickFile(editor, "", true, uploadAttachmentAction, true) - } - > - {t("File attachment")} - + {!templateMode && ( + } + onClick={() => + pickFile(editor, "", true, uploadAttachmentAction, true) + } + > + {t("File attachment")} + + )} ); diff --git a/apps/client/src/features/editor/components/fixed-toolbar/groups/more-inserts-group.tsx b/apps/client/src/features/editor/components/fixed-toolbar/groups/more-inserts-group.tsx index 86a452206..ce23b7b4f 100644 --- a/apps/client/src/features/editor/components/fixed-toolbar/groups/more-inserts-group.tsx +++ b/apps/client/src/features/editor/components/fixed-toolbar/groups/more-inserts-group.tsx @@ -32,9 +32,10 @@ import { useTranslation } from "react-i18next"; interface Props { editor: Editor; + templateMode?: boolean; } -export const MoreInsertsGroup: FC = ({ editor }) => { +export const MoreInsertsGroup: FC = ({ editor, templateMode }) => { const { t } = useTranslation(); const setEmbed = (provider: string) => @@ -91,14 +92,16 @@ export const MoreInsertsGroup: FC = ({ editor }) => { > {t("Subpages")} - } - onClick={() => - editor.chain().focus().insertTransclusionSource().run() - } - > - {t("Synced block")} - + {!templateMode && ( + } + onClick={() => + editor.chain().focus().insertTransclusionSource().run() + } + > + {t("Synced block")} + + )} {t("Diagrams")} @@ -115,18 +118,22 @@ export const MoreInsertsGroup: FC = ({ editor }) => { > {t("Mermaid diagram")} - } - onClick={() => editor.chain().focus().setDrawio().run()} - > - Draw.io - - } - onClick={() => editor.chain().focus().setExcalidraw().run()} - > - Excalidraw - + {!templateMode && ( + } + onClick={() => editor.chain().focus().setDrawio().run()} + > + Draw.io + + )} + {!templateMode && ( + } + onClick={() => editor.chain().focus().setExcalidraw().run()} + > + Excalidraw + + )} {t("Embeds")} diff --git a/apps/client/src/features/editor/extensions/extensions.ts b/apps/client/src/features/editor/extensions/extensions.ts index f991b653c..87c7b9e5f 100644 --- a/apps/client/src/features/editor/extensions/extensions.ts +++ b/apps/client/src/features/editor/extensions/extensions.ts @@ -3,7 +3,7 @@ import { StarterKit } from "@tiptap/starter-kit"; import { Code } from "@tiptap/extension-code"; import { TextAlign } from "@tiptap/extension-text-align"; import { TaskList, TaskItem } from "@tiptap/extension-list"; -import { Placeholder, CharacterCount } from "@tiptap/extensions"; +import { Placeholder, CharacterCount, UndoRedo } from "@tiptap/extensions"; import { Superscript } from "@tiptap/extension-superscript"; import SubScript from "@tiptap/extension-subscript"; import { Typography } from "@tiptap/extension-typography"; @@ -437,6 +437,7 @@ const TemplateSlashCommand = Command.configure({ export const templateExtensions = [ ...mainExtensions.filter((ext: any) => ext !== SlashCommand), TemplateSlashCommand, + UndoRedo, ] as any; export const collabExtensions: CollabExtensions = (provider, user) => [