feat: editor UI refresh and enhancements (#1968)

* feat: new image menu
* switch to resizable side handles
* use pixels

* refactor excalidraw and drawio menu

* support image resize undo

* video resize

* callout menu refresh

* refresh table menus

* fix color scheme

* fix: patch @tiptap/core ResizableNodeView to prevent resize sticking after mouseup

* feat: columns

* notes callout

* focus on first column

* capture tab key in column

* fix print

* hide columns menu when some nodes are focused

* fix print

* fix columns

* selective placeholder

* fix blockquote

* quote

* fix callout in columns
This commit is contained in:
Philip Okugbe
2026-02-24 15:22:37 +00:00
committed by GitHub
parent c172d3bd5e
commit ef87210b3d
42 changed files with 3082 additions and 533 deletions
@@ -43,6 +43,8 @@ import {
Highlight,
UniqueID,
SharedStorage,
Columns,
Column,
} from "@docmost/editor-ext";
import {
randomElement,
@@ -52,6 +54,14 @@ import { IUser } from "@/features/user/types/user.types.ts";
import MathInlineView from "@/features/editor/components/math/math-inline.tsx";
import MathBlockView from "@/features/editor/components/math/math-block.tsx";
import ImageView from "@/features/editor/components/image/image-view.tsx";
import {
createImageHandle,
imageResizeClasses,
} from "@/features/editor/components/image/image-resize-handles.ts";
import {
createResizeHandle,
buildResizeClasses,
} from "@/features/editor/components/common/node-resize-handles.ts";
import CalloutView from "@/features/editor/components/callout/callout-view.tsx";
import VideoView from "@/features/editor/components/video/video-view.tsx";
import AttachmentView from "@/features/editor/components/attachment/attachment-view.tsx";
@@ -91,6 +101,7 @@ lowlight.register("fortran", fortran);
lowlight.register("haskell", haskell);
lowlight.register("scala", scala);
// @ts-ignore
export const mainExtensions = [
StarterKit.configure({
heading: false,
@@ -115,7 +126,7 @@ export const mainExtensions = [
filterTransaction: (transaction) => !isChangeOrigin(transaction),
}),
Placeholder.configure({
placeholder: ({ node }) => {
placeholder: ({ editor, node, pos }) => {
if (node.type.name === "heading") {
return i18n.t("Heading {{level}}", { level: node.attrs.level });
}
@@ -123,6 +134,17 @@ export const mainExtensions = [
return i18n.t("Toggle title");
}
if (node.type.name === "paragraph") {
const $pos = editor.state.doc.resolve(pos);
const parentName = $pos.parent.type.name;
if (
parentName === "column" ||
parentName === "tableCell" ||
parentName === "tableHeader" ||
parentName === "callout" ||
parentName === "blockquote"
) {
return i18n.t("Write...");
}
return i18n.t('Write anything. Enter "/" for commands');
}
},
@@ -200,9 +222,29 @@ export const mainExtensions = [
TiptapImage.configure({
view: ImageView,
allowBase64: false,
resize: {
enabled: true,
directions: ["left", "right"],
minWidth: 80,
minHeight: 40,
alwaysPreserveAspectRatio: true,
//@ts-ignore
createCustomHandle: createImageHandle,
className: imageResizeClasses,
},
}),
TiptapVideo.configure({
view: VideoView,
resize: {
enabled: true,
directions: ["left", "right"],
minWidth: 80,
minHeight: 40,
alwaysPreserveAspectRatio: true,
//@ts-ignore
createCustomHandle: createResizeHandle,
className: buildResizeClasses("node-video"),
},
}),
Callout.configure({
view: CalloutView,
@@ -221,9 +263,29 @@ export const mainExtensions = [
}),
Drawio.configure({
view: DrawioView,
resize: {
enabled: true,
directions: ["left", "right"],
minWidth: 80,
minHeight: 40,
alwaysPreserveAspectRatio: true,
//@ts-ignore
createCustomHandle: createResizeHandle,
className: buildResizeClasses("node-drawio"),
},
}),
Excalidraw.configure({
view: ExcalidrawView,
resize: {
enabled: true,
directions: ["left", "right"],
minWidth: 80,
minHeight: 40,
alwaysPreserveAspectRatio: true,
//@ts-ignore
createCustomHandle: createResizeHandle,
className: buildResizeClasses("node-excalidraw"),
},
}),
Embed.configure({
view: EmbedView,
@@ -253,6 +315,8 @@ export const mainExtensions = [
};
},
}).configure(),
Columns,
Column,
] as any;
type CollabExtensions = (provider: HocuspocusProvider, user: IUser) => any[];