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) => [