diff --git a/packages/frappe-ui-react/src/components/textEditor/textEditor.css b/packages/frappe-ui-react/src/components/textEditor/textEditor.css
index b17456c7..d446a04a 100644
--- a/packages/frappe-ui-react/src/components/textEditor/textEditor.css
+++ b/packages/frappe-ui-react/src/components/textEditor/textEditor.css
@@ -139,3 +139,114 @@ img.ProseMirror-selectednode {
.tag-suggestion-active {
background-color: var(--color-surface-gray-2, #f3f3f3);
}
+
+/* CodeBlock styles */
+.code-block {
+ position: relative;
+}
+
+.code-block-container {
+ position: relative;
+}
+
+.language-selector {
+ position: absolute;
+ top: 0.25rem;
+ right: 0.25rem;
+ padding-top: 0;
+ padding-bottom: 0;
+ opacity: 0;
+ z-index: 10;
+ transition-property: opacity;
+ transition-duration: 0.2s;
+ transition-timing-function: ease-in-out;
+ transition-delay: 0s;
+ pointer-events: none;
+}
+
+.code-block-container:hover .language-selector {
+ opacity: 1;
+ transition-delay: 0s;
+ pointer-events: auto;
+}
+
+.language-selector:focus-within {
+ opacity: 1;
+ transition-delay: 0s;
+ pointer-events: auto;
+}
+
+/* When mouse leaves the code block, delay the hiding */
+.code-block-container:not(:hover) .language-selector:not(:focus-within) {
+ transition-delay: 1.5s;
+}
+
+.ProseMirror pre {
+ background: #0d0d0d;
+ color: #fff;
+ font-family: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono',
+ 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Mono', 'Source Code Pro', 'Fira Mono',
+ 'Droid Sans Mono', 'Consolas', 'Courier New', monospace;
+ padding: 0.75rem 1rem;
+ border-radius: 0.75rem;
+ caret-color: #fff;
+}
+
+.ProseMirror pre code {
+ color: inherit;
+ padding: 0;
+ background: none;
+ font-size: 12px;
+}
+
+.ProseMirror pre .hljs-comment,
+.ProseMirror pre .hljs-quote {
+ color: #999;
+}
+
+.ProseMirror pre .hljs-variable,
+.ProseMirror pre .hljs-template-variable,
+.ProseMirror pre .hljs-attribute,
+.ProseMirror pre .hljs-tag,
+.ProseMirror pre .hljs-name,
+.ProseMirror pre .hljs-regexp,
+.ProseMirror pre .hljs-link,
+.ProseMirror pre .hljs-selector-id,
+.ProseMirror pre .hljs-selector-class {
+ color: #f2777a;
+}
+
+.ProseMirror pre .hljs-number,
+.ProseMirror pre .hljs-meta,
+.ProseMirror pre .hljs-built_in,
+.ProseMirror pre .hljs-builtin-name,
+.ProseMirror pre .hljs-literal,
+.ProseMirror pre .hljs-type,
+.ProseMirror pre .hljs-params {
+ color: #f99157;
+}
+
+.ProseMirror pre .hljs-string,
+.ProseMirror pre .hljs-symbol,
+.ProseMirror pre .hljs-bullet {
+ color: #99cc99;
+}
+
+.ProseMirror pre .hljs-title,
+.ProseMirror pre .hljs-section {
+ color: #ffcc66;
+}
+
+.ProseMirror pre .hljs-keyword,
+.ProseMirror pre .hljs-selector-tag {
+ color: #6196cc;
+}
+
+.ProseMirror pre .hljs-emphasis {
+ font-style: italic;
+}
+
+.ProseMirror pre .hljs-strong {
+ font-weight: 700;
+}
+
diff --git a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
index a8b577f7..b7d9a1b9 100644
--- a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
+++ b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
@@ -5,6 +5,8 @@ import { EditorContent, EditorContext, useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { TaskItem, TaskList } from "@tiptap/extension-list";
import TextAlign from "@tiptap/extension-text-align";
+import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight";
+import { createLowlight, all } from "lowlight";
import Strike from "@tiptap/extension-strike";
import clsx from "clsx";
@@ -16,6 +18,8 @@ import { normalizeClasses } from "../../utils";
import type { TextEditorProps } from "./types";
import FixedMenu from "./menu/fixedMenu";
+const lowlight = createLowlight(all);
+
const TextEditor = ({ content, editorClass = "" }: TextEditorProps) => {
const editor = useEditor({
content,
@@ -28,7 +32,10 @@ const TextEditor = ({ content, editorClass = "" }: TextEditorProps) => {
},
},
extensions: [
- StarterKit,
+ StarterKit.configure({
+ strike: false,
+ codeBlock: false,
+ }),
TaskList,
TaskItem.configure({
nested: true,
@@ -37,6 +44,9 @@ const TextEditor = ({ content, editorClass = "" }: TextEditorProps) => {
types: ["heading", "paragraph"],
}),
Strike,
+ CodeBlockLowlight.configure({
+ lowlight,
+ }),
],
});
From fec25dd0911987f04f6cfc8995068f7487e066fa Mon Sep 17 00:00:00 2001
From: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
Date: Mon, 26 Jan 2026 16:26:07 +0530
Subject: [PATCH 14/48] feat: add horizontal rule
Signed-off-by: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
---
packages/frappe-ui-react/package.json | 1 +
.../src/components/textEditor/menu/commands/index.ts | 7 +++++++
.../src/components/textEditor/menu/commands/types.ts | 1 +
.../src/components/textEditor/menu/menu.tsx | 1 +
.../src/components/textEditor/textEditor.tsx | 2 ++
5 files changed, 12 insertions(+)
diff --git a/packages/frappe-ui-react/package.json b/packages/frappe-ui-react/package.json
index 4807c29e..d25a6970 100644
--- a/packages/frappe-ui-react/package.json
+++ b/packages/frappe-ui-react/package.json
@@ -46,6 +46,7 @@
"@floating-ui/react": "^0.27.16",
"@headlessui/react": "^2.2.9",
"@popperjs/core": "^2.11.8",
+ "@tiptap/extension-horizontal-rule": "^3.17.1",
"@tiptap/extension-list": "^3.17.1",
"@tiptap/extension-strike": "^3.17.1",
"@tiptap/extension-task-list": "^3.17.1",
diff --git a/packages/frappe-ui-react/src/components/textEditor/menu/commands/index.ts b/packages/frappe-ui-react/src/components/textEditor/menu/commands/index.ts
index 88320058..ee4c3c7e 100644
--- a/packages/frappe-ui-react/src/components/textEditor/menu/commands/index.ts
+++ b/packages/frappe-ui-react/src/components/textEditor/menu/commands/index.ts
@@ -16,6 +16,7 @@ import {
ListCheckIcon,
ListIcon,
ListOrderedIcon,
+ SeparatorHorizontal,
StrikethroughIcon,
TypeIcon,
} from "lucide-react";
@@ -134,6 +135,12 @@ export const COMMANDS: Record
= {
action: (editor) => editor.chain().focus().toggleStrike().run(),
isActive: (editor) => editor.isActive("strike"),
},
+ horizontal_rule: {
+ label: "Horizontal Rule",
+ icon: SeparatorHorizontal,
+ action: (editor) => editor.chain().focus().setHorizontalRule().run(),
+ isActive: (editor) => editor.isActive("strike"),
+ },
};
export default COMMANDS;
diff --git a/packages/frappe-ui-react/src/components/textEditor/menu/commands/types.ts b/packages/frappe-ui-react/src/components/textEditor/menu/commands/types.ts
index 9b113688..245fbdca 100644
--- a/packages/frappe-ui-react/src/components/textEditor/menu/commands/types.ts
+++ b/packages/frappe-ui-react/src/components/textEditor/menu/commands/types.ts
@@ -20,6 +20,7 @@ export const COMMANDS_KEYS = [
"align_center",
"align_right",
"strike",
+ "horizontal_rule",
] as const;
export type TYPE_COMMANDS_KEYS = (typeof COMMANDS_KEYS)[number];
diff --git a/packages/frappe-ui-react/src/components/textEditor/menu/menu.tsx b/packages/frappe-ui-react/src/components/textEditor/menu/menu.tsx
index 5c394921..1156c9ac 100644
--- a/packages/frappe-ui-react/src/components/textEditor/menu/menu.tsx
+++ b/packages/frappe-ui-react/src/components/textEditor/menu/menu.tsx
@@ -41,6 +41,7 @@ const DEFAULT_COMMANDS: Array<
"align_center",
"align_right",
"separator",
+ "horizontal_rule",
];
const Menu = ({ className }: MenuProps) => {
diff --git a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
index a8b577f7..e3782084 100644
--- a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
+++ b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
@@ -5,6 +5,7 @@ import { EditorContent, EditorContext, useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { TaskItem, TaskList } from "@tiptap/extension-list";
import TextAlign from "@tiptap/extension-text-align";
+import HorizontalRule from "@tiptap/extension-horizontal-rule";
import Strike from "@tiptap/extension-strike";
import clsx from "clsx";
@@ -37,6 +38,7 @@ const TextEditor = ({ content, editorClass = "" }: TextEditorProps) => {
types: ["heading", "paragraph"],
}),
Strike,
+ HorizontalRule,
],
});
From 8cd9b42058ab6c13ce5ae7f87185b8bf65246cd2 Mon Sep 17 00:00:00 2001
From: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
Date: Mon, 26 Jan 2026 16:48:26 +0530
Subject: [PATCH 15/48] feat: add table extension
Signed-off-by: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
---
packages/frappe-ui-react/package.json | 1 +
.../textEditor/menu/commands/index.ts | 84 +++++++++++++++++++
.../textEditor/menu/commands/types.ts | 16 +++-
.../src/components/textEditor/menu/menu.tsx | 25 +++++-
.../src/components/textEditor/textEditor.tsx | 2 +
5 files changed, 124 insertions(+), 4 deletions(-)
diff --git a/packages/frappe-ui-react/package.json b/packages/frappe-ui-react/package.json
index 4807c29e..84deee7e 100644
--- a/packages/frappe-ui-react/package.json
+++ b/packages/frappe-ui-react/package.json
@@ -48,6 +48,7 @@
"@popperjs/core": "^2.11.8",
"@tiptap/extension-list": "^3.17.1",
"@tiptap/extension-strike": "^3.17.1",
+ "@tiptap/extension-table": "^3.17.1",
"@tiptap/extension-task-list": "^3.17.1",
"@tiptap/extension-text-align": "^3.17.1",
"@tiptap/pm": "^3.17.1",
diff --git a/packages/frappe-ui-react/src/components/textEditor/menu/commands/index.ts b/packages/frappe-ui-react/src/components/textEditor/menu/commands/index.ts
index 88320058..1125a564 100644
--- a/packages/frappe-ui-react/src/components/textEditor/menu/commands/index.ts
+++ b/packages/frappe-ui-react/src/components/textEditor/menu/commands/index.ts
@@ -17,6 +17,7 @@ import {
ListIcon,
ListOrderedIcon,
StrikethroughIcon,
+ TableIcon,
TypeIcon,
} from "lucide-react";
@@ -134,6 +135,89 @@ export const COMMANDS: Record = {
action: (editor) => editor.chain().focus().toggleStrike().run(),
isActive: (editor) => editor.isActive("strike"),
},
+ insert_table: {
+ label: "Insert Table",
+ icon: TableIcon,
+ action: (editor) =>
+ editor
+ .chain()
+ .focus()
+ .insertTable({ rows: 3, cols: 3, withHeaderRow: true })
+ .run(),
+ isActive: () => false,
+ },
+ add_column_before: {
+ label: "Add Column Before",
+ action: (editor) => editor.chain().focus().addColumnBefore().run(),
+ isActive: () => false,
+ isDisabled: (editor) => !editor.can().addColumnBefore(),
+ },
+ add_column_after: {
+ label: "Add Column After",
+ action: (editor) => editor.chain().focus().addColumnAfter().run(),
+ isActive: () => false,
+ isDisabled: (editor) => !editor.can().addColumnAfter(),
+ },
+ delete_column: {
+ label: "Delete Column",
+ action: (editor) => editor.chain().focus().deleteColumn().run(),
+ isActive: () => false,
+ isDisabled: (editor) => !editor.can().deleteColumn(),
+ },
+ add_row_before: {
+ label: "Add Row Before",
+ action: (editor) => editor.chain().focus().addRowBefore().run(),
+ isActive: () => false,
+ isDisabled: (editor) => !editor.can().addRowBefore(),
+ },
+ add_row_after: {
+ label: "Add Row After",
+ action: (editor) => editor.chain().focus().addRowAfter().run(),
+ isActive: () => false,
+ isDisabled: (editor) => !editor.can().addRowAfter(),
+ },
+ delete_row: {
+ label: "Delete Row",
+ action: (editor) => editor.chain().focus().deleteRow().run(),
+ isActive: () => false,
+ isDisabled: (editor) => !editor.can().deleteRow(),
+ },
+ delete_table: {
+ label: "Delete Table",
+ action: (editor) => editor.chain().focus().deleteTable().run(),
+ isActive: () => false,
+ isDisabled: (editor) => !editor.can().deleteTable(),
+ },
+ merge_cells: {
+ label: "Merge Cells",
+ action: (editor) => editor.chain().focus().mergeCells().run(),
+ isActive: () => false,
+ isDisabled: (editor) => !editor.can().mergeCells(),
+ },
+ split_cell: {
+ label: "Split Cell",
+ action: (editor) => editor.chain().focus().splitCell().run(),
+ isActive: () => false,
+ isDisabled: (editor) => !editor.can().splitCell(),
+ },
+ toggle_header_column: {
+ label: "Toggle Header Column",
+ action: (editor) => editor.chain().focus().toggleHeaderColumn().run(),
+ isActive: () => false,
+ isDisabled: (editor) => !editor.can().toggleHeaderColumn(),
+ },
+ toggle_header_row: {
+ label: "Toggle Header Row",
+ action: (editor) => editor.chain().focus().toggleHeaderRow().run(),
+ isActive: () => false,
+ isDisabled: (editor) => !editor.can().toggleHeaderRow(),
+ },
+ toggle_header_cell: {
+ label: "Toggle Header Cell",
+ action: (editor) => editor.chain().focus().toggleHeaderCell().run(),
+ isActive: () => false,
+ isDisabled: (editor) => !editor.can().toggleHeaderCell(),
+ },
};
export default COMMANDS;
diff --git a/packages/frappe-ui-react/src/components/textEditor/menu/commands/types.ts b/packages/frappe-ui-react/src/components/textEditor/menu/commands/types.ts
index 9b113688..750d39b7 100644
--- a/packages/frappe-ui-react/src/components/textEditor/menu/commands/types.ts
+++ b/packages/frappe-ui-react/src/components/textEditor/menu/commands/types.ts
@@ -20,6 +20,19 @@ export const COMMANDS_KEYS = [
"align_center",
"align_right",
"strike",
+ "insert_table",
+ "add_column_before",
+ "add_column_after",
+ "delete_column",
+ "add_row_before",
+ "add_row_after",
+ "delete_row",
+ "merge_cells",
+ "split_cell",
+ "toggle_header_column",
+ "toggle_header_row",
+ "toggle_header_cell",
+ "delete_table",
] as const;
export type TYPE_COMMANDS_KEYS = (typeof COMMANDS_KEYS)[number];
@@ -27,7 +40,8 @@ export type TYPE_COMMANDS_KEYS = (typeof COMMANDS_KEYS)[number];
export interface EditorCommand {
label: string;
text?: string;
- icon: React.ComponentType<{ className?: string }>;
+ icon?: React.ComponentType<{ className?: string }>;
action: (editor: Editor) => void;
isActive: (editor: Editor) => boolean;
+ isDisabled?: (editor: Editor) => boolean;
}
diff --git a/packages/frappe-ui-react/src/components/textEditor/menu/menu.tsx b/packages/frappe-ui-react/src/components/textEditor/menu/menu.tsx
index 5c394921..57be5351 100644
--- a/packages/frappe-ui-react/src/components/textEditor/menu/menu.tsx
+++ b/packages/frappe-ui-react/src/components/textEditor/menu/menu.tsx
@@ -41,6 +41,19 @@ const DEFAULT_COMMANDS: Array<
"align_center",
"align_right",
"separator",
+ [
+ "insert_table",
+ "add_column_before",
+ "add_column_after",
+ "delete_column",
+ "add_row_before",
+ "add_row_after",
+ "delete_row",
+ "toggle_header_column",
+ "toggle_header_row",
+ "toggle_header_cell",
+ "delete_table",
+ ],
];
const Menu = ({ className }: MenuProps) => {
@@ -89,13 +102,17 @@ const Menu = ({ className }: MenuProps) => {
className="rounded px-2 py-1 text-base font-medium text-ink-gray-8 transition-colors hover:bg-surface-gray-2"
onClick={() => togglePopover()}
>
-
+ {ActiveIcon && }
)}
body={({ close }) => (
{command_key.map((command_key, optionIndex) => {
const command = COMMANDS[command_key];
+ const isDisabled = command.isDisabled?.(editor);
+ if (isDisabled) {
+ return null;
+ }
return (
-
);
})}
diff --git a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
index a8b577f7..b26524cd 100644
--- a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
+++ b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
@@ -6,6 +6,7 @@ import StarterKit from "@tiptap/starter-kit";
import { TaskItem, TaskList } from "@tiptap/extension-list";
import TextAlign from "@tiptap/extension-text-align";
import Strike from "@tiptap/extension-strike";
+import { TableKit } from "@tiptap/extension-table";
import clsx from "clsx";
/**
@@ -37,6 +38,7 @@ const TextEditor = ({ content, editorClass = "" }: TextEditorProps) => {
types: ["heading", "paragraph"],
}),
Strike,
+ TableKit,
],
});
From 11db0cafc54d6abed8105dd1998e8a21e04045f4 Mon Sep 17 00:00:00 2001
From: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
Date: Mon, 26 Jan 2026 19:36:19 +0530
Subject: [PATCH 16/48] feat: bind event handling
Signed-off-by: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
---
.../src/components/textEditor/textEditor.tsx | 21 ++++++++++++++++++-
.../src/components/textEditor/types.ts | 4 +++-
2 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
index a8b577f7..2f46573f 100644
--- a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
+++ b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
@@ -16,7 +16,14 @@ import { normalizeClasses } from "../../utils";
import type { TextEditorProps } from "./types";
import FixedMenu from "./menu/fixedMenu";
-const TextEditor = ({ content, editorClass = "" }: TextEditorProps) => {
+const TextEditor = ({
+ content,
+ editorClass = "",
+ onChange,
+ onFocus,
+ onBlur,
+ onTransaction,
+}: TextEditorProps) => {
const editor = useEditor({
content,
editorProps: {
@@ -38,6 +45,18 @@ const TextEditor = ({ content, editorClass = "" }: TextEditorProps) => {
}),
Strike,
],
+ onUpdate: ({ editor }) => {
+ onChange?.(editor.getHTML());
+ },
+ onFocus: ({ event }) => {
+ onFocus?.(event);
+ },
+ onBlur: ({ event }) => {
+ onBlur?.(event);
+ },
+ onTransaction: () => {
+ onTransaction?.(editor);
+ },
});
return (
diff --git a/packages/frappe-ui-react/src/components/textEditor/types.ts b/packages/frappe-ui-react/src/components/textEditor/types.ts
index e39d9cca..0ca90619 100644
--- a/packages/frappe-ui-react/src/components/textEditor/types.ts
+++ b/packages/frappe-ui-react/src/components/textEditor/types.ts
@@ -4,6 +4,7 @@
import type { Editor } from "@tiptap/react";
export interface TextEditorProps {
+ // Props
content?: string | null;
placeholder?: string | (() => string);
editorClass?: string | string[] | Record;
@@ -13,9 +14,10 @@ export interface TextEditorProps {
bubbleMenuOptions?: Record;
fixedMenu?: boolean;
floatingMenu?: boolean;
+ className?: string;
+ // Events
onChange?: (content: string) => void;
onFocus?: (event: FocusEvent) => void;
onBlur?: (event: FocusEvent) => void;
onTransaction?: (editor: Editor) => void;
- className?: string;
}
From 8e5772d750d6d73004b787beae5be132bc27ce17 Mon Sep 17 00:00:00 2001
From: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
Date: Mon, 26 Jan 2026 19:40:36 +0530
Subject: [PATCH 17/48] feat: add placeholder handling
Signed-off-by: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
---
packages/frappe-ui-react/package.json | 1 +
.../src/components/textEditor/textEditor.tsx | 6 ++++++
2 files changed, 7 insertions(+)
diff --git a/packages/frappe-ui-react/package.json b/packages/frappe-ui-react/package.json
index 4807c29e..41e6cf1e 100644
--- a/packages/frappe-ui-react/package.json
+++ b/packages/frappe-ui-react/package.json
@@ -47,6 +47,7 @@
"@headlessui/react": "^2.2.9",
"@popperjs/core": "^2.11.8",
"@tiptap/extension-list": "^3.17.1",
+ "@tiptap/extension-placeholder": "^3.17.1",
"@tiptap/extension-strike": "^3.17.1",
"@tiptap/extension-task-list": "^3.17.1",
"@tiptap/extension-text-align": "^3.17.1",
diff --git a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
index 2f46573f..5dfcd87d 100644
--- a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
+++ b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
@@ -15,9 +15,11 @@ import "./textEditor.css";
import { normalizeClasses } from "../../utils";
import type { TextEditorProps } from "./types";
import FixedMenu from "./menu/fixedMenu";
+import Placeholder from "@tiptap/extension-placeholder";
const TextEditor = ({
content,
+ placeholder = "",
editorClass = "",
onChange,
onFocus,
@@ -36,6 +38,10 @@ const TextEditor = ({
},
extensions: [
StarterKit,
+ Placeholder.configure({
+ placeholder:
+ typeof placeholder === "function" ? placeholder() : placeholder,
+ }),
TaskList,
TaskItem.configure({
nested: true,
From 26615af7109cc043654bf4001e43ac6076989057 Mon Sep 17 00:00:00 2001
From: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
Date: Mon, 26 Jan 2026 19:41:44 +0530
Subject: [PATCH 18/48] feat: bind editable prop
Signed-off-by: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
---
.../frappe-ui-react/src/components/textEditor/textEditor.tsx | 2 ++
1 file changed, 2 insertions(+)
diff --git a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
index 5dfcd87d..f30a2ec1 100644
--- a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
+++ b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
@@ -21,6 +21,7 @@ const TextEditor = ({
content,
placeholder = "",
editorClass = "",
+ editable = true,
onChange,
onFocus,
onBlur,
@@ -28,6 +29,7 @@ const TextEditor = ({
}: TextEditorProps) => {
const editor = useEditor({
content,
+ editable,
editorProps: {
attributes: {
class: clsx(
From f7abf8a10052eba0409c796166f36ecf307ac77c Mon Sep 17 00:00:00 2001
From: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
Date: Mon, 26 Jan 2026 19:45:45 +0530
Subject: [PATCH 19/48] feat: bind more props
Signed-off-by: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
---
.../src/components/textEditor/textEditor.tsx | 9 ++++++++-
.../frappe-ui-react/src/components/textEditor/types.ts | 10 ++++------
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
index f30a2ec1..e0ca5d09 100644
--- a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
+++ b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
@@ -22,6 +22,9 @@ const TextEditor = ({
placeholder = "",
editorClass = "",
editable = true,
+ autofocus = false,
+ extensions = [],
+ starterKitOptions = {},
onChange,
onFocus,
onBlur,
@@ -30,6 +33,7 @@ const TextEditor = ({
const editor = useEditor({
content,
editable,
+ autofocus,
editorProps: {
attributes: {
class: clsx(
@@ -39,7 +43,9 @@ const TextEditor = ({
},
},
extensions: [
- StarterKit,
+ StarterKit.configure({
+ ...starterKitOptions,
+ }),
Placeholder.configure({
placeholder:
typeof placeholder === "function" ? placeholder() : placeholder,
@@ -52,6 +58,7 @@ const TextEditor = ({
types: ["heading", "paragraph"],
}),
Strike,
+ ...extensions,
],
onUpdate: ({ editor }) => {
onChange?.(editor.getHTML());
diff --git a/packages/frappe-ui-react/src/components/textEditor/types.ts b/packages/frappe-ui-react/src/components/textEditor/types.ts
index 0ca90619..7b35b815 100644
--- a/packages/frappe-ui-react/src/components/textEditor/types.ts
+++ b/packages/frappe-ui-react/src/components/textEditor/types.ts
@@ -1,7 +1,8 @@
/**
* External dependencies.
*/
-import type { Editor } from "@tiptap/react";
+import type { Editor, Extension } from "@tiptap/react";
+import type { StarterKitOptions } from "@tiptap/starter-kit";
export interface TextEditorProps {
// Props
@@ -10,11 +11,8 @@ export interface TextEditorProps {
editorClass?: string | string[] | Record;
editable?: boolean;
autofocus?: boolean;
- bubbleMenu?: boolean;
- bubbleMenuOptions?: Record;
- fixedMenu?: boolean;
- floatingMenu?: boolean;
- className?: string;
+ extensions?: Extension[];
+ starterKitOptions?: Partial;
// Events
onChange?: (content: string) => void;
onFocus?: (event: FocusEvent) => void;
From 1d5367d61da268fc2742a8c6863c65582138d83d Mon Sep 17 00:00:00 2001
From: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
Date: Mon, 26 Jan 2026 19:52:50 +0530
Subject: [PATCH 20/48] feat: partially bind fixed menu prop
Signed-off-by: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
---
.../src/components/textEditor/texEditor.stories.tsx | 7 ++++++-
.../src/components/textEditor/textEditor.tsx | 4 +++-
.../frappe-ui-react/src/components/textEditor/types.ts | 1 +
3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/packages/frappe-ui-react/src/components/textEditor/texEditor.stories.tsx b/packages/frappe-ui-react/src/components/textEditor/texEditor.stories.tsx
index b5e461b7..da2bcc13 100644
--- a/packages/frappe-ui-react/src/components/textEditor/texEditor.stories.tsx
+++ b/packages/frappe-ui-react/src/components/textEditor/texEditor.stories.tsx
@@ -37,7 +37,12 @@ export const Basic: Story = {
`);
return (
-
+
);
},
diff --git a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
index e0ca5d09..e0100bb1 100644
--- a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
+++ b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
@@ -25,6 +25,7 @@ const TextEditor = ({
autofocus = false,
extensions = [],
starterKitOptions = {},
+ fixedMenu = false,
onChange,
onFocus,
onBlur,
@@ -76,7 +77,8 @@ const TextEditor = ({
return (
-
+ {fixedMenu && }
+
);
diff --git a/packages/frappe-ui-react/src/components/textEditor/types.ts b/packages/frappe-ui-react/src/components/textEditor/types.ts
index 7b35b815..a1f20f70 100644
--- a/packages/frappe-ui-react/src/components/textEditor/types.ts
+++ b/packages/frappe-ui-react/src/components/textEditor/types.ts
@@ -13,6 +13,7 @@ export interface TextEditorProps {
autofocus?: boolean;
extensions?: Extension[];
starterKitOptions?: Partial;
+ fixedMenu?: boolean;
// Events
onChange?: (content: string) => void;
onFocus?: (event: FocusEvent) => void;
From d09ed0ea7b1be5b2be2d708ee41370ce33f540b4 Mon Sep 17 00:00:00 2001
From: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
Date: Mon, 26 Jan 2026 19:58:45 +0530
Subject: [PATCH 21/48] feat: bind slots
Signed-off-by: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
---
.../src/components/textEditor/textEditor.tsx | 8 ++++++--
.../frappe-ui-react/src/components/textEditor/types.ts | 7 ++++++-
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
index e0100bb1..d9d622b3 100644
--- a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
+++ b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
@@ -30,6 +30,9 @@ const TextEditor = ({
onFocus,
onBlur,
onTransaction,
+ Top,
+ Editor,
+ Bottom,
}: TextEditorProps) => {
const editor = useEditor({
content,
@@ -77,9 +80,10 @@ const TextEditor = ({
return (
+ {Top && }
{fixedMenu && }
-
-
+ {Editor ? : }
+ {Bottom && }
);
};
diff --git a/packages/frappe-ui-react/src/components/textEditor/types.ts b/packages/frappe-ui-react/src/components/textEditor/types.ts
index a1f20f70..b8165118 100644
--- a/packages/frappe-ui-react/src/components/textEditor/types.ts
+++ b/packages/frappe-ui-react/src/components/textEditor/types.ts
@@ -1,8 +1,9 @@
/**
* External dependencies.
*/
-import type { Editor, Extension } from "@tiptap/react";
+import { Editor, type Extension } from "@tiptap/react";
import type { StarterKitOptions } from "@tiptap/starter-kit";
+import type { FC } from "react";
export interface TextEditorProps {
// Props
@@ -19,4 +20,8 @@ export interface TextEditorProps {
onFocus?: (event: FocusEvent) => void;
onBlur?: (event: FocusEvent) => void;
onTransaction?: (editor: Editor) => void;
+ // Slots
+ Top?: FC;
+ Editor?: FC<{ editor: Editor }>;
+ Bottom?: FC;
}
From c3d46af33a61dfbdfbe6d0f5ad4721ad37be0ad0 Mon Sep 17 00:00:00 2001
From: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
Date: Tue, 27 Jan 2026 10:51:39 +0530
Subject: [PATCH 22/48] chore: update lock file
---
pnpm-lock.yaml | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ba350dc9..9ab042e6 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -168,6 +168,9 @@ importers:
'@tiptap/extension-list':
specifier: ^3.17.1
version: 3.17.1(@tiptap/core@3.17.1(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)
+ '@tiptap/extension-placeholder':
+ specifier: ^3.17.1
+ version: 3.17.1(@tiptap/extensions@3.17.1(@tiptap/core@3.17.1(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1))
'@tiptap/extension-strike':
specifier: ^3.17.1
version: 3.17.1(@tiptap/core@3.17.1(@tiptap/pm@3.17.1))
@@ -2133,6 +2136,11 @@ packages:
peerDependencies:
'@tiptap/core': ^3.17.1
+ '@tiptap/extension-placeholder@3.17.1':
+ resolution: {integrity: sha512-cE8Rij5/1t4KnWE7GaDewhBek9DKNB+97yrxyggMegILg6v195hOmOkRZkyfnFMYZoBDlrfSAtX9wBvbZBqIsg==}
+ peerDependencies:
+ '@tiptap/extensions': ^3.17.1
+
'@tiptap/extension-strike@3.17.1':
resolution: {integrity: sha512-c6fS6YIhxoU55etlJgM0Xqker+jn7I1KC7GVu6ljmda8I00K3/lOLZgvFUNPmgp8EJWtyTctj+3D3D+PaZaFAA==}
peerDependencies:
@@ -7160,6 +7168,10 @@ snapshots:
dependencies:
'@tiptap/core': 3.17.1(@tiptap/pm@3.17.1)
+ '@tiptap/extension-placeholder@3.17.1(@tiptap/extensions@3.17.1(@tiptap/core@3.17.1(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1))':
+ dependencies:
+ '@tiptap/extensions': 3.17.1(@tiptap/core@3.17.1(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)
+
'@tiptap/extension-strike@3.17.1(@tiptap/core@3.17.1(@tiptap/pm@3.17.1))':
dependencies:
'@tiptap/core': 3.17.1(@tiptap/pm@3.17.1)
From 1e468751949f59c005c343af10d7409c8fbacbf8 Mon Sep 17 00:00:00 2001
From: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
Date: Tue, 27 Jan 2026 11:05:55 +0530
Subject: [PATCH 23/48] chore: update lock file
---
pnpm-lock.yaml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9ab042e6..e6f5f2b7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -165,6 +165,9 @@ importers:
'@popperjs/core':
specifier: ^2.11.8
version: 2.11.8
+ '@tiptap/extension-horizontal-rule':
+ specifier: ^3.17.1
+ version: 3.17.1(@tiptap/core@3.17.1(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)
'@tiptap/extension-list':
specifier: ^3.17.1
version: 3.17.1(@tiptap/core@3.17.1(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)
From bf30f1dd6516e34eb522466adcdbac324ae2aeb7 Mon Sep 17 00:00:00 2001
From: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
Date: Tue, 27 Jan 2026 11:39:02 +0530
Subject: [PATCH 24/48] chore: fix lock file
---
pnpm-lock.yaml | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9ab042e6..08d00d8e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -174,6 +174,9 @@ importers:
'@tiptap/extension-strike':
specifier: ^3.17.1
version: 3.17.1(@tiptap/core@3.17.1(@tiptap/pm@3.17.1))
+ '@tiptap/extension-table':
+ specifier: ^3.17.1
+ version: 3.17.1(@tiptap/core@3.17.1(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)
'@tiptap/extension-task-list':
specifier: ^3.17.1
version: 3.17.1(@tiptap/extension-list@3.17.1(@tiptap/core@3.17.1(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1))
@@ -2146,6 +2149,12 @@ packages:
peerDependencies:
'@tiptap/core': ^3.17.1
+ '@tiptap/extension-table@3.17.1':
+ resolution: {integrity: sha512-FuAMdmM330tHJUYT5IV2ooFRqtXf+0D8llcE9nIQQCXKL4J0pfGSOIm40LVpunYgx2pV8SSCL51qTBuEmR84tQ==}
+ peerDependencies:
+ '@tiptap/core': ^3.17.1
+ '@tiptap/pm': ^3.17.1
+
'@tiptap/extension-task-list@3.17.1':
resolution: {integrity: sha512-EiJjlfNioQub9G6SZ83b/1+5VhVtlHStQN7+BTIsA9EhRAeKwge0KtO1oMIzFkDryd9mki9GQx/D+eek2CNsYw==}
peerDependencies:
@@ -7176,6 +7185,11 @@ snapshots:
dependencies:
'@tiptap/core': 3.17.1(@tiptap/pm@3.17.1)
+ '@tiptap/extension-table@3.17.1(@tiptap/core@3.17.1(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)':
+ dependencies:
+ '@tiptap/core': 3.17.1(@tiptap/pm@3.17.1)
+ '@tiptap/pm': 3.17.1
+
'@tiptap/extension-task-list@3.17.1(@tiptap/extension-list@3.17.1(@tiptap/core@3.17.1(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1))':
dependencies:
'@tiptap/extension-list': 3.17.1(@tiptap/core@3.17.1(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)
From c4b0ac64cfbd14c39742aa0fcc8bdf83338aed05 Mon Sep 17 00:00:00 2001
From: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
Date: Tue, 27 Jan 2026 17:07:36 +0530
Subject: [PATCH 25/48] chore: load typography plugin
Signed-off-by: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
---
packages/frappe-ui-react/src/utils/tailwind.config.cjs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/frappe-ui-react/src/utils/tailwind.config.cjs b/packages/frappe-ui-react/src/utils/tailwind.config.cjs
index 21cc402a..93e09861 100644
--- a/packages/frappe-ui-react/src/utils/tailwind.config.cjs
+++ b/packages/frappe-ui-react/src/utils/tailwind.config.cjs
@@ -9,5 +9,5 @@ module.exports = {
theme: {
extend,
},
- plugins: [],
+ plugins: [require("@tailwindcss/typography")],
};
From f928a8292ae5a39d1e2c37d1049ee7b8c70f927e Mon Sep 17 00:00:00 2001
From: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
Date: Tue, 27 Jan 2026 17:24:28 +0530
Subject: [PATCH 26/48] fix:story
Signed-off-by: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
---
.../src/components/textEditor/menu/menu.tsx | 3 +-
.../textEditor/texEditor.stories.tsx | 64 +++++++++++---
.../src/components/textEditor/textEditor.tsx | 88 ++++++++++---------
.../src/components/textEditor/types.ts | 2 +-
4 files changed, 99 insertions(+), 58 deletions(-)
diff --git a/packages/frappe-ui-react/src/components/textEditor/menu/menu.tsx b/packages/frappe-ui-react/src/components/textEditor/menu/menu.tsx
index 5c394921..04697f47 100644
--- a/packages/frappe-ui-react/src/components/textEditor/menu/menu.tsx
+++ b/packages/frappe-ui-react/src/components/textEditor/menu/menu.tsx
@@ -1,7 +1,6 @@
/**
* External dependencies.
*/
-
import { useCurrentEditor, useEditorState } from "@tiptap/react";
import clsx from "clsx";
@@ -67,7 +66,7 @@ const Menu = ({ className }: MenuProps) => {
return (
diff --git a/packages/frappe-ui-react/src/components/textEditor/texEditor.stories.tsx b/packages/frappe-ui-react/src/components/textEditor/texEditor.stories.tsx
index da2bcc13..c6809b4f 100644
--- a/packages/frappe-ui-react/src/components/textEditor/texEditor.stories.tsx
+++ b/packages/frappe-ui-react/src/components/textEditor/texEditor.stories.tsx
@@ -1,8 +1,6 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
import TextEditor from "./textEditor";
-import { useState } from "react";
-
const meta: Meta
= {
title: "Components/TextEditor",
component: TextEditor,
@@ -16,6 +14,50 @@ const meta: Meta = {
control: "text",
description: "HTML content of the editor",
},
+ placeholder: {
+ control: "text",
+ description: "Placeholder text when editor is empty",
+ },
+ editorClass: {
+ control: "text",
+ description: "CSS classes to apply to the editor content area",
+ },
+ editable: {
+ control: "boolean",
+ description: "Whether the editor is editable",
+ },
+ autofocus: {
+ control: "boolean",
+ description: "Whether to autofocus the editor on mount",
+ },
+ extensions: {
+ control: false,
+ description: "Additional TipTap extensions",
+ },
+ starterkitOptions: {
+ control: "object",
+ description: "Configuration for StarterKit extension",
+ },
+ fixedMenu: {
+ control: "boolean",
+ description: "Show fixed menu toolbar",
+ },
+ onChange: {
+ action: "changed",
+ description: "Callback when content changes",
+ },
+ onFocus: {
+ action: "focused",
+ description: "Callback when editor receives focus",
+ },
+ onBlur: {
+ action: "blurred",
+ description: "Callback when editor loses focus",
+ },
+ onTransaction: {
+ control: false,
+ description: "Callback on editor transaction",
+ },
},
};
@@ -23,10 +65,7 @@ export default meta;
type Story = StoryObj;
export const Basic: Story = {
args: {
- editorClass: "prose-sm min-h-[4rem] border rounded-b-lg border-t-0 p-2",
- },
- render: function BasicRender(args) {
- const [content, setContent] = useState(`
+ content: `
Heading 2
This is a paragraph with bold and italic text.
@@ -34,15 +73,14 @@ export const Basic: Story = {
`);
+ `,
+ editorClass: "prose-sm min-h-[4rem] border rounded-b-lg border-t-0 p-2",
+ fixedMenu: true,
+ },
+ render: function BasicRender(args) {
return (
-
+
);
},
diff --git a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
index d9d622b3..140a2e29 100644
--- a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
+++ b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
@@ -24,7 +24,7 @@ const TextEditor = ({
editable = true,
autofocus = false,
extensions = [],
- starterKitOptions = {},
+ starterkitOptions = {},
fixedMenu = false,
onChange,
onFocus,
@@ -34,49 +34,53 @@ const TextEditor = ({
Editor,
Bottom,
}: TextEditorProps) => {
- const editor = useEditor({
- content,
- editable,
- autofocus,
- editorProps: {
- attributes: {
- class: clsx(
- "prose prose-table:table-fixed prose-td:p-2 prose-th:p-2 prose-td:border prose-th:border prose-td:border-outline-gray-2 prose-th:border-outline-gray-2 prose-td:relative prose-th:relative prose-th:bg-surface-gray-2 border-outline-gray-1",
- normalizeClasses(editorClass)
- ),
+ const editor = useEditor(
+ {
+ content,
+ editable,
+ autofocus,
+ editorProps: {
+ attributes: {
+ class: clsx(
+ "prose prose-table:table-fixed prose-td:p-2 prose-th:p-2 prose-td:border prose-th:border prose-td:border-outline-gray-2 prose-th:border-outline-gray-2 prose-td:relative prose-th:relative prose-th:bg-surface-gray-2 border-outline-gray-1",
+ normalizeClasses(editorClass)
+ ),
+ },
+ },
+ extensions: [
+ StarterKit.configure({
+ strike: false,
+ ...starterkitOptions,
+ }),
+ Placeholder.configure({
+ placeholder:
+ typeof placeholder === "function" ? placeholder() : placeholder,
+ }),
+ TaskList,
+ TaskItem.configure({
+ nested: true,
+ }),
+ TextAlign.configure({
+ types: ["heading", "paragraph"],
+ }),
+ Strike,
+ ...extensions,
+ ],
+ onUpdate: ({ editor }) => {
+ onChange?.(editor.getHTML());
+ },
+ onFocus: ({ event }) => {
+ onFocus?.(event);
+ },
+ onBlur: ({ event }) => {
+ onBlur?.(event);
+ },
+ onTransaction: () => {
+ onTransaction?.(editor);
},
},
- extensions: [
- StarterKit.configure({
- ...starterKitOptions,
- }),
- Placeholder.configure({
- placeholder:
- typeof placeholder === "function" ? placeholder() : placeholder,
- }),
- TaskList,
- TaskItem.configure({
- nested: true,
- }),
- TextAlign.configure({
- types: ["heading", "paragraph"],
- }),
- Strike,
- ...extensions,
- ],
- onUpdate: ({ editor }) => {
- onChange?.(editor.getHTML());
- },
- onFocus: ({ event }) => {
- onFocus?.(event);
- },
- onBlur: ({ event }) => {
- onBlur?.(event);
- },
- onTransaction: () => {
- onTransaction?.(editor);
- },
- });
+ [content, editable, autofocus, editorClass, starterkitOptions, extensions]
+ );
return (
diff --git a/packages/frappe-ui-react/src/components/textEditor/types.ts b/packages/frappe-ui-react/src/components/textEditor/types.ts
index b8165118..6388efbe 100644
--- a/packages/frappe-ui-react/src/components/textEditor/types.ts
+++ b/packages/frappe-ui-react/src/components/textEditor/types.ts
@@ -13,7 +13,7 @@ export interface TextEditorProps {
editable?: boolean;
autofocus?: boolean;
extensions?: Extension[];
- starterKitOptions?: Partial;
+ starterkitOptions?: Partial;
fixedMenu?: boolean;
// Events
onChange?: (content: string) => void;
From 07745aee70d6d8ece7ca098e4553fa9fa09c25c0 Mon Sep 17 00:00:00 2001
From: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
Date: Tue, 27 Jan 2026 17:27:58 +0530
Subject: [PATCH 27/48] fix: add callbacks to dependency array
Signed-off-by: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
---
.../src/components/textEditor/textEditor.tsx | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
index 140a2e29..f3855baf 100644
--- a/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
+++ b/packages/frappe-ui-react/src/components/textEditor/textEditor.tsx
@@ -79,7 +79,18 @@ const TextEditor = ({
onTransaction?.(editor);
},
},
- [content, editable, autofocus, editorClass, starterkitOptions, extensions]
+ [
+ content,
+ editable,
+ autofocus,
+ editorClass,
+ starterkitOptions,
+ extensions,
+ onChange,
+ onFocus,
+ onBlur,
+ onTransaction,
+ ]
);
return (
From 0f8cdb3a9b2294787477f62618e5da59addf90cf Mon Sep 17 00:00:00 2001
From: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
Date: Wed, 28 Jan 2026 13:36:24 +0530
Subject: [PATCH 28/48] refactor: update editor command type
Signed-off-by: ayushnirwal <53055971+ayushnirwal@users.noreply.github.com>
---
.../src/components/textEditor/menu/commands/types.ts | 9 +++++++--
.../src/components/textEditor/menu/menu.tsx | 4 ++--
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/packages/frappe-ui-react/src/components/textEditor/menu/commands/types.ts b/packages/frappe-ui-react/src/components/textEditor/menu/commands/types.ts
index 9b113688..e633c3f5 100644
--- a/packages/frappe-ui-react/src/components/textEditor/menu/commands/types.ts
+++ b/packages/frappe-ui-react/src/components/textEditor/menu/commands/types.ts
@@ -2,6 +2,7 @@
* External dependencies.
*/
import type { Editor } from "@tiptap/react";
+import type { JSX } from "react";
export const COMMANDS_KEYS = [
"paragraph",
@@ -20,6 +21,7 @@ export const COMMANDS_KEYS = [
"align_center",
"align_right",
"strike",
+ "iframe",
] as const;
export type TYPE_COMMANDS_KEYS = (typeof COMMANDS_KEYS)[number];
@@ -27,7 +29,10 @@ export type TYPE_COMMANDS_KEYS = (typeof COMMANDS_KEYS)[number];
export interface EditorCommand {
label: string;
text?: string;
- icon: React.ComponentType<{ className?: string }>;
- action: (editor: Editor) => void;
+ icon: ({ className }: { className?: string }) => JSX.Element;
+ action?: (editor: Editor) => void;
isActive: (editor: Editor) => boolean;
+ component?: React.FC<{
+ editor: Editor;
+ }>;
}
diff --git a/packages/frappe-ui-react/src/components/textEditor/menu/menu.tsx b/packages/frappe-ui-react/src/components/textEditor/menu/menu.tsx
index 04697f47..4c883597 100644
--- a/packages/frappe-ui-react/src/components/textEditor/menu/menu.tsx
+++ b/packages/frappe-ui-react/src/components/textEditor/menu/menu.tsx
@@ -100,7 +100,7 @@ const Menu = ({ className }: MenuProps) => {