dishui 0.0.78 → 0.0.79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,30 +1,30 @@
1
1
  import { cn as e } from "../../../utils/cn.js";
2
2
  import { MarkdownBlockExtension as t } from "./extensions/MarkdownBlock.js";
3
3
  import { FloatingDialogExtension as n } from "./extensions/FloatingDialog.js";
4
- import ee from "./Toolbar/Toolbar.js";
5
- import r from "./AIRewrite.js";
6
- import i from "./ContextMenu.js";
7
- import a from "./CommentPanel.js";
8
- import o from "./CommentDialog.js";
9
- import s from "./CommentPopover.js";
10
- import c from "./SlashMenu.js";
11
- import te from "./BubbleToolbar.js";
12
- import ne from "./BlockHandle.js";
13
- import { CodeBlockExtension as re } from "./extensions/CodeBlockView.js";
14
- import ie from "./extensions/FontSize.js";
4
+ import r from "./Toolbar/Toolbar.js";
5
+ import i from "./AIRewrite.js";
6
+ import a from "./ContextMenu.js";
7
+ import o from "./CommentPanel.js";
8
+ import s from "./CommentDialog.js";
9
+ import c from "./CommentPopover.js";
10
+ import l from "./SlashMenu.js";
11
+ import ee from "./BubbleToolbar.js";
12
+ import te from "./BlockHandle.js";
13
+ import { CodeBlockExtension as ne } from "./extensions/CodeBlockView.js";
14
+ import re from "./extensions/FontSize.js";
15
+ import { ResizableImage as ie } from "./extensions/ResizableImage.js";
15
16
  import { CommentMark as ae } from "./extensions/CommentMark.js";
16
17
  /* empty css */
17
- import { useCallback as l, useEffect as u, useRef as oe, useState as d } from "react";
18
- import { jsx as f, jsxs as p } from "react/jsx-runtime";
19
- import { EditorContent as m, useEditor as se } from "@tiptap/react";
20
- import ce from "@tiptap/starter-kit";
21
- import le from "@tiptap/extension-underline";
22
- import { TextStyle as ue } from "@tiptap/extension-text-style";
23
- import de from "@tiptap/extension-color";
24
- import fe from "@tiptap/extension-highlight";
25
- import pe from "@tiptap/extension-text-align";
26
- import me from "@tiptap/extension-link";
27
- import he from "@tiptap/extension-image";
18
+ import { useCallback as u, useEffect as d, useRef as oe, useState as f } from "react";
19
+ import { jsx as p, jsxs as m } from "react/jsx-runtime";
20
+ import { EditorContent as se, useEditor as ce } from "@tiptap/react";
21
+ import le from "@tiptap/starter-kit";
22
+ import ue from "@tiptap/extension-underline";
23
+ import { TextStyle as de } from "@tiptap/extension-text-style";
24
+ import fe from "@tiptap/extension-color";
25
+ import pe from "@tiptap/extension-highlight";
26
+ import me from "@tiptap/extension-text-align";
27
+ import he from "@tiptap/extension-link";
28
28
  import { Table as ge } from "@tiptap/extension-table";
29
29
  import _e from "@tiptap/extension-table-row";
30
30
  import ve from "@tiptap/extension-table-cell";
@@ -41,15 +41,15 @@ function C() {
41
41
  return `c_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
42
42
  }
43
43
  var w = ({ content: w, value: T, onChange: E, onChangeJSON: D, placeholder: O = "Start writing...", editable: k = !0, aiConfig: A, toolbarConfig: j, minHeight: M = "200px", maxHeight: N, className: P = "", autofocus: F = !1, labels: I, editorRef: L, notionMode: R = !1, showComments: ye = !1, commentThreads: be, onCommentThreadsChange: xe, currentUser: z }) => {
44
- let [Se, Ce] = d(!1), [we, B] = d(!1), [Te, Ee] = d(""), [De, Oe] = d({
44
+ let [Se, Ce] = f(!1), [we, B] = f(!1), [Te, Ee] = f(""), [De, Oe] = f({
45
45
  from: 0,
46
46
  to: 0
47
- }), [V, H] = d(R), [U, W] = d(ye), [ke, Ae] = d([]), [je, G] = d(!1), [K, Me] = d({
47
+ }), [V, H] = f(R), [U, W] = f(ye), [ke, Ae] = f([]), [je, G] = f(!1), [K, Me] = f({
48
48
  from: 0,
49
49
  to: 0
50
- }), q = oe(null), J = be ?? ke, Y = xe ?? Ae, X = se({
50
+ }), q = oe(null), J = be ?? ke, Y = xe ?? Ae, X = ce({
51
51
  extensions: [
52
- ce.configure({
52
+ le.configure({
53
53
  codeBlock: !1,
54
54
  heading: { levels: [
55
55
  1,
@@ -62,17 +62,17 @@ var w = ({ content: w, value: T, onChange: E, onChangeJSON: D, placeholder: O =
62
62
  link: !1,
63
63
  underline: !1
64
64
  }),
65
- le,
66
65
  ue,
67
66
  de,
68
- fe.configure({ multicolor: !0 }),
69
- pe.configure({ types: ["heading", "paragraph"] }),
70
- me.configure({
67
+ fe,
68
+ pe.configure({ multicolor: !0 }),
69
+ me.configure({ types: ["heading", "paragraph"] }),
70
+ he.configure({
71
71
  openOnClick: !1,
72
72
  autolink: !0,
73
73
  HTMLAttributes: { class: "rte-link" }
74
74
  }),
75
- he.configure({
75
+ ie.configure({
76
76
  inline: !1,
77
77
  allowBase64: !0
78
78
  }),
@@ -81,14 +81,14 @@ var w = ({ content: w, value: T, onChange: E, onChangeJSON: D, placeholder: O =
81
81
  ve,
82
82
  h,
83
83
  g.configure({ placeholder: O }),
84
- re,
84
+ ne,
85
85
  _,
86
86
  v.configure({ nested: !0 }),
87
87
  y,
88
88
  b,
89
89
  x,
90
90
  S,
91
- ie,
91
+ re,
92
92
  n,
93
93
  t,
94
94
  ae
@@ -100,28 +100,28 @@ var w = ({ content: w, value: T, onChange: E, onChangeJSON: D, placeholder: O =
100
100
  E?.(e.getHTML()), D?.(e.getJSON());
101
101
  }
102
102
  });
103
- u(() => {
103
+ d(() => {
104
104
  L && X && (L.current = X);
105
- }, [X, L]), u(() => {
105
+ }, [X, L]), d(() => {
106
106
  X && T !== void 0 && T !== X.getHTML() && X.commands.setContent(T, { emitUpdate: !1 });
107
- }, [X, T]), u(() => {
107
+ }, [X, T]), d(() => {
108
108
  X && X.setEditable(k);
109
109
  }, [X, k]);
110
- let Z = l(() => {
110
+ let Z = u(() => {
111
111
  if (!X) return;
112
112
  let { from: e, to: t } = X.state.selection, n = e === t ? "" : X.state.doc.textBetween(e, t, " ");
113
113
  Ee(n), Oe({
114
114
  from: e,
115
115
  to: t
116
116
  }), B(!0);
117
- }, [X]), Q = l(() => {
117
+ }, [X]), Q = u(() => {
118
118
  if (!X) return;
119
119
  let { from: e, to: t } = X.state.selection;
120
120
  e !== t && (Me({
121
121
  from: e,
122
122
  to: t
123
123
  }), G(!0));
124
- }, [X]), Ne = l((e) => {
124
+ }, [X]), Ne = u((e) => {
125
125
  if (!X) return;
126
126
  let t = C(), n = {
127
127
  id: t,
@@ -141,40 +141,40 @@ var w = ({ content: w, value: T, onChange: E, onChangeJSON: D, placeholder: O =
141
141
  J,
142
142
  Y,
143
143
  z
144
- ]), Pe = l((e) => {
144
+ ]), Pe = u((e) => {
145
145
  q.current?.scrollToThread(e);
146
146
  }, []);
147
147
  if (!X) return null;
148
- let Fe = typeof M == "number" ? `${M}px` : M, Ie = N ? typeof N == "number" ? `${N}px` : N : void 0, Le = e("rte-container", Se && "rte-container--format-painter", V && "rte-container--notion", P), Re = J.filter((e) => !e.resolved).length, $ = /* @__PURE__ */ p("div", {
148
+ let Fe = typeof M == "number" ? `${M}px` : M, Ie = N ? typeof N == "number" ? `${N}px` : N : void 0, Le = e("rte-container", Se && "rte-container--format-painter", V && "rte-container--notion", P), Re = J.filter((e) => !e.resolved).length, $ = /* @__PURE__ */ m("div", {
149
149
  className: "rte-mode-toggle",
150
150
  children: [
151
- /* @__PURE__ */ f("button", {
151
+ /* @__PURE__ */ p("button", {
152
152
  type: "button",
153
153
  className: `rte-mode-toggle__btn ${V ? "" : "rte-mode-toggle__btn--active"}`,
154
154
  onClick: () => H(!1),
155
155
  title: "Classic mode",
156
156
  children: "Classic"
157
157
  }),
158
- /* @__PURE__ */ f("button", {
158
+ /* @__PURE__ */ p("button", {
159
159
  type: "button",
160
160
  className: `rte-mode-toggle__btn ${V ? "rte-mode-toggle__btn--active" : ""}`,
161
161
  onClick: () => H(!0),
162
162
  title: "Notion-style mode",
163
163
  children: "Notion"
164
164
  }),
165
- /* @__PURE__ */ f("div", {
165
+ /* @__PURE__ */ p("div", {
166
166
  className: "rte-toolbar__divider",
167
167
  style: {
168
168
  height: 20,
169
169
  margin: "0 4px"
170
170
  }
171
171
  }),
172
- /* @__PURE__ */ p("button", {
172
+ /* @__PURE__ */ m("button", {
173
173
  type: "button",
174
174
  className: `rte-toolbar__btn rte-toolbar__comment-btn ${U ? "rte-toolbar__btn--active" : ""}`,
175
175
  onClick: () => W(!U),
176
176
  title: "Toggle comments",
177
- children: [/* @__PURE__ */ f("svg", {
177
+ children: [/* @__PURE__ */ p("svg", {
178
178
  width: "16",
179
179
  height: "16",
180
180
  viewBox: "0 0 24 24",
@@ -183,19 +183,19 @@ var w = ({ content: w, value: T, onChange: E, onChangeJSON: D, placeholder: O =
183
183
  strokeWidth: "2",
184
184
  strokeLinecap: "round",
185
185
  strokeLinejoin: "round",
186
- children: /* @__PURE__ */ f("path", { d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z" })
187
- }), Re > 0 && /* @__PURE__ */ f("span", { className: "rte-toolbar__comment-badge" })]
186
+ children: /* @__PURE__ */ p("path", { d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z" })
187
+ }), Re > 0 && /* @__PURE__ */ p("span", { className: "rte-toolbar__comment-badge" })]
188
188
  })
189
189
  ]
190
190
  });
191
- return /* @__PURE__ */ p("div", {
191
+ return /* @__PURE__ */ m("div", {
192
192
  className: Le,
193
193
  style: {
194
194
  minHeight: Fe,
195
195
  maxHeight: Ie
196
196
  },
197
197
  children: [
198
- k && !V && /* @__PURE__ */ f(ee, {
198
+ k && !V && /* @__PURE__ */ p(r, {
199
199
  editor: X,
200
200
  config: j,
201
201
  labels: I,
@@ -204,36 +204,36 @@ var w = ({ content: w, value: T, onChange: E, onChangeJSON: D, placeholder: O =
204
204
  onFormatPainterChange: Ce,
205
205
  children: $
206
206
  }),
207
- k && V && /* @__PURE__ */ f("div", {
207
+ k && V && /* @__PURE__ */ p("div", {
208
208
  className: "rte-notion-topbar",
209
209
  children: $
210
210
  }),
211
- /* @__PURE__ */ p("div", {
211
+ /* @__PURE__ */ m("div", {
212
212
  style: {
213
213
  display: "flex",
214
214
  flex: 1,
215
215
  overflow: "hidden"
216
216
  },
217
- children: [/* @__PURE__ */ p("div", {
217
+ children: [/* @__PURE__ */ m("div", {
218
218
  className: "rte-editor-wrapper",
219
219
  style: { flex: 1 },
220
220
  children: [
221
- /* @__PURE__ */ f(m, {
221
+ /* @__PURE__ */ p(se, {
222
222
  editor: X,
223
223
  className: "rte-editor-content"
224
224
  }),
225
- k && /* @__PURE__ */ f(i, {
225
+ k && /* @__PURE__ */ p(a, {
226
226
  editor: X,
227
227
  labels: I,
228
228
  onAIClick: A ? Z : void 0
229
229
  }),
230
- k && /* @__PURE__ */ f(c, { editor: X }),
231
- k && V && /* @__PURE__ */ f(te, {
230
+ k && /* @__PURE__ */ p(l, { editor: X }),
231
+ k && V && /* @__PURE__ */ p(ee, {
232
232
  editor: X,
233
233
  onAddComment: Q
234
234
  }),
235
- k && V && /* @__PURE__ */ f(ne, { editor: X }),
236
- /* @__PURE__ */ f(s, {
235
+ k && V && /* @__PURE__ */ p(te, { editor: X }),
236
+ /* @__PURE__ */ p(c, {
237
237
  editor: X,
238
238
  threads: J,
239
239
  onThreadsChange: Y,
@@ -243,7 +243,7 @@ var w = ({ content: w, value: T, onChange: E, onChangeJSON: D, placeholder: O =
243
243
  currentUser: z
244
244
  })
245
245
  ]
246
- }), U && /* @__PURE__ */ f(a, {
246
+ }), U && /* @__PURE__ */ p(o, {
247
247
  ref: q,
248
248
  editor: X,
249
249
  threads: J,
@@ -252,7 +252,7 @@ var w = ({ content: w, value: T, onChange: E, onChangeJSON: D, placeholder: O =
252
252
  currentUser: z
253
253
  })]
254
254
  }),
255
- A && we && /* @__PURE__ */ f(r, {
255
+ A && we && /* @__PURE__ */ p(i, {
256
256
  editor: X,
257
257
  aiConfig: A,
258
258
  labels: I,
@@ -260,7 +260,7 @@ var w = ({ content: w, value: T, onChange: E, onChangeJSON: D, placeholder: O =
260
260
  selectionRange: De,
261
261
  onClose: () => B(!1)
262
262
  }),
263
- /* @__PURE__ */ f(o, {
263
+ /* @__PURE__ */ p(s, {
264
264
  open: je,
265
265
  onSubmit: Ne,
266
266
  onClose: () => G(!1)
@@ -1 +1 @@
1
- {"version":3,"file":"RichTextEditor.js","names":[],"sources":["../../../../src/components/pro/RichTextEditor/RichTextEditor.tsx"],"sourcesContent":["import React, { useEffect, useCallback, useState, useRef } from 'react';\nimport { cn } from '../../../utils/cn';\nimport { useEditor, EditorContent } from '@tiptap/react';\nimport StarterKit from '@tiptap/starter-kit';\nimport Underline from '@tiptap/extension-underline';\nimport { TextStyle } from '@tiptap/extension-text-style';\nimport Color from '@tiptap/extension-color';\nimport Highlight from '@tiptap/extension-highlight';\nimport TextAlign from '@tiptap/extension-text-align';\nimport Link from '@tiptap/extension-link';\nimport Image from '@tiptap/extension-image';\nimport { Table } from '@tiptap/extension-table';\nimport TableRow from '@tiptap/extension-table-row';\nimport TableCell from '@tiptap/extension-table-cell';\nimport TableHeader from '@tiptap/extension-table-header';\nimport Placeholder from '@tiptap/extension-placeholder';\nimport TaskList from '@tiptap/extension-task-list';\nimport TaskItem from '@tiptap/extension-task-item';\nimport Subscript from '@tiptap/extension-subscript';\nimport Superscript from '@tiptap/extension-superscript';\nimport Typography from '@tiptap/extension-typography';\nimport FontFamily from '@tiptap/extension-font-family';\n\nimport Toolbar from './Toolbar';\nimport AIRewrite from './AIRewrite';\nimport ContextMenu from './ContextMenu';\nimport CommentPanel from './CommentPanel';\nimport type { CommentPanelHandle } from './CommentPanel';\nimport CommentDialog from './CommentDialog';\nimport CommentPopover from './CommentPopover';\nimport SlashMenu from './SlashMenu';\nimport BubbleToolbar from './BubbleToolbar';\nimport BlockHandle from './BlockHandle';\nimport { MarkdownBlockExtension } from './extensions/MarkdownBlock';\nimport { CodeBlockExtension } from './extensions/CodeBlockView';\nimport FontSize from './extensions/FontSize';\nimport { FloatingDialogExtension } from './extensions/FloatingDialog';\nimport { CommentMark } from './extensions/CommentMark';\nimport type { CommentThread } from './extensions/CommentMark';\nimport type { RichTextEditorProps } from './types';\nimport './styles.css';\n\nfunction generateId() {\n return `c_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\nconst RichTextEditor: React.FC<RichTextEditorProps> = ({\n content,\n value,\n onChange,\n onChangeJSON,\n placeholder = 'Start writing...',\n editable = true,\n aiConfig,\n toolbarConfig,\n minHeight = '200px',\n maxHeight,\n className = '',\n autofocus = false,\n labels,\n editorRef,\n notionMode: initialNotionMode = false,\n showComments: initialShowComments = false,\n commentThreads: externalThreads,\n onCommentThreadsChange,\n currentUser,\n}) => {\n const [formatPainterActive, setFormatPainterActive] = useState(false);\n const [showAIPanel, setShowAIPanel] = useState(false);\n const [aiSelection, setAiSelection] = useState('');\n const [aiSelectionRange, setAiSelectionRange] = useState({ from: 0, to: 0 });\n const [notionMode, setNotionMode] = useState(initialNotionMode);\n const [showComments, setShowComments] = useState(initialShowComments);\n const [internalThreads, setInternalThreads] = useState<CommentThread[]>([]);\n const [commentDialogOpen, setCommentDialogOpen] = useState(false);\n const [commentSelectionRange, setCommentSelectionRange] = useState({ from: 0, to: 0 });\n const commentPanelRef = useRef<CommentPanelHandle>(null);\n\n const threads = externalThreads ?? internalThreads;\n const setThreads = onCommentThreadsChange ?? setInternalThreads;\n\n const editor = useEditor({\n extensions: [\n StarterKit.configure({\n codeBlock: false,\n heading: { levels: [1, 2, 3, 4, 5, 6] },\n // v3 StarterKit bundles these; explicit extensions below stay in charge.\n link: false,\n underline: false,\n }),\n Underline,\n TextStyle,\n Color,\n Highlight.configure({ multicolor: true }),\n TextAlign.configure({\n types: ['heading', 'paragraph'],\n }),\n Link.configure({\n openOnClick: false,\n autolink: true,\n HTMLAttributes: { class: 'rte-link' },\n }),\n Image.configure({ inline: false, allowBase64: true }),\n Table.configure({ resizable: true }),\n TableRow,\n TableCell,\n TableHeader,\n Placeholder.configure({ placeholder }),\n CodeBlockExtension,\n TaskList,\n TaskItem.configure({ nested: true }),\n Subscript,\n Superscript,\n Typography,\n FontFamily,\n FontSize,\n FloatingDialogExtension,\n MarkdownBlockExtension,\n CommentMark,\n ],\n content: value || content || '',\n editable,\n autofocus: autofocus ? 'end' : false,\n onUpdate: ({ editor: e }) => {\n onChange?.(e.getHTML());\n onChangeJSON?.(e.getJSON() as Record<string, unknown>);\n },\n });\n\n useEffect(() => {\n if (editorRef && editor) {\n editorRef.current = editor;\n }\n }, [editor, editorRef]);\n\n useEffect(() => {\n if (editor && value !== undefined) {\n const currentHTML = editor.getHTML();\n if (value !== currentHTML) {\n editor.commands.setContent(value, { emitUpdate: false });\n }\n }\n }, [editor, value]);\n\n useEffect(() => {\n if (editor) {\n editor.setEditable(editable);\n }\n }, [editor, editable]);\n\n const handleAIClick = useCallback(() => {\n if (!editor) return;\n const { from, to } = editor.state.selection;\n const text = from !== to\n ? editor.state.doc.textBetween(from, to, ' ')\n : '';\n setAiSelection(text);\n setAiSelectionRange({ from, to });\n setShowAIPanel(true);\n }, [editor]);\n\n const requestAddComment = useCallback(() => {\n if (!editor) return;\n const { from, to } = editor.state.selection;\n if (from === to) return;\n setCommentSelectionRange({ from, to });\n setCommentDialogOpen(true);\n }, [editor]);\n\n const handleCommentSubmit = useCallback((text: string) => {\n if (!editor) return;\n const id = generateId();\n const thread: CommentThread = {\n id,\n resolved: false,\n comments: [{\n id: generateId(),\n author: currentUser || 'User',\n text,\n createdAt: Date.now(),\n }],\n createdAt: Date.now(),\n };\n editor.chain()\n .focus()\n .setTextSelection(commentSelectionRange)\n .setComment(id)\n .run();\n setThreads([...threads, thread]);\n setCommentDialogOpen(false);\n }, [editor, commentSelectionRange, threads, setThreads, currentUser]);\n\n const handleScrollToThread = useCallback((id: string) => {\n commentPanelRef.current?.scrollToThread(id);\n }, []);\n\n if (!editor) return null;\n\n const minH = typeof minHeight === 'number' ? `${minHeight}px` : minHeight;\n const maxH = maxHeight ? (typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight) : undefined;\n\n const containerClass = cn(\n 'rte-container',\n formatPainterActive && 'rte-container--format-painter',\n notionMode && 'rte-container--notion',\n className,\n );\n\n const activeCommentCount = threads.filter((t) => !t.resolved).length;\n\n const modeToggle = (\n <div className=\"rte-mode-toggle\">\n <button\n type=\"button\"\n className={`rte-mode-toggle__btn ${!notionMode ? 'rte-mode-toggle__btn--active' : ''}`}\n onClick={() => setNotionMode(false)}\n title=\"Classic mode\"\n >\n Classic\n </button>\n <button\n type=\"button\"\n className={`rte-mode-toggle__btn ${notionMode ? 'rte-mode-toggle__btn--active' : ''}`}\n onClick={() => setNotionMode(true)}\n title=\"Notion-style mode\"\n >\n Notion\n </button>\n <div className=\"rte-toolbar__divider\" style={{ height: 20, margin: '0 4px' }} />\n <button\n type=\"button\"\n className={`rte-toolbar__btn rte-toolbar__comment-btn ${showComments ? 'rte-toolbar__btn--active' : ''}`}\n onClick={() => setShowComments(!showComments)}\n title=\"Toggle comments\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z\" />\n </svg>\n {activeCommentCount > 0 && <span className=\"rte-toolbar__comment-badge\" />}\n </button>\n </div>\n );\n\n return (\n <div className={containerClass} style={{ minHeight: minH, maxHeight: maxH }}>\n {editable && !notionMode && (\n <Toolbar\n editor={editor}\n config={toolbarConfig}\n labels={labels}\n hasAI={!!aiConfig}\n onAIClick={handleAIClick}\n onFormatPainterChange={setFormatPainterActive}\n >\n {modeToggle}\n </Toolbar>\n )}\n\n {editable && notionMode && (\n <div className=\"rte-notion-topbar\">\n {modeToggle}\n </div>\n )}\n\n <div style={{ display: 'flex', flex: 1, overflow: 'hidden' }}>\n <div className=\"rte-editor-wrapper\" style={{ flex: 1 }}>\n <EditorContent editor={editor} className=\"rte-editor-content\" />\n {editable && <ContextMenu editor={editor} labels={labels} onAIClick={aiConfig ? handleAIClick : undefined} />}\n {editable && <SlashMenu editor={editor} />}\n {editable && notionMode && <BubbleToolbar editor={editor} onAddComment={requestAddComment} />}\n {editable && notionMode && <BlockHandle editor={editor} />}\n <CommentPopover\n editor={editor}\n threads={threads}\n onThreadsChange={setThreads}\n showPanel={showComments}\n onOpenPanel={() => setShowComments(true)}\n onScrollToThread={handleScrollToThread}\n currentUser={currentUser}\n />\n </div>\n\n {showComments && (\n <CommentPanel\n ref={commentPanelRef}\n editor={editor}\n threads={threads}\n onThreadsChange={setThreads}\n onRequestAddComment={requestAddComment}\n currentUser={currentUser}\n />\n )}\n </div>\n\n {aiConfig && showAIPanel && (\n <AIRewrite\n editor={editor}\n aiConfig={aiConfig}\n labels={labels}\n initialSelection={aiSelection}\n selectionRange={aiSelectionRange}\n onClose={() => setShowAIPanel(false)}\n />\n )}\n\n <CommentDialog\n open={commentDialogOpen}\n onSubmit={handleCommentSubmit}\n onClose={() => setCommentDialogOpen(false)}\n />\n </div>\n );\n};\n\nexport default RichTextEditor;\n"],"mappings":"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"}
1
+ {"version":3,"file":"RichTextEditor.js","names":[],"sources":["../../../../src/components/pro/RichTextEditor/RichTextEditor.tsx"],"sourcesContent":["import React, { useEffect, useCallback, useState, useRef } from 'react';\nimport { cn } from '../../../utils/cn';\nimport { useEditor, EditorContent } from '@tiptap/react';\nimport StarterKit from '@tiptap/starter-kit';\nimport Underline from '@tiptap/extension-underline';\nimport { TextStyle } from '@tiptap/extension-text-style';\nimport Color from '@tiptap/extension-color';\nimport Highlight from '@tiptap/extension-highlight';\nimport TextAlign from '@tiptap/extension-text-align';\nimport Link from '@tiptap/extension-link';\nimport { Table } from '@tiptap/extension-table';\nimport TableRow from '@tiptap/extension-table-row';\nimport TableCell from '@tiptap/extension-table-cell';\nimport TableHeader from '@tiptap/extension-table-header';\nimport Placeholder from '@tiptap/extension-placeholder';\nimport TaskList from '@tiptap/extension-task-list';\nimport TaskItem from '@tiptap/extension-task-item';\nimport Subscript from '@tiptap/extension-subscript';\nimport Superscript from '@tiptap/extension-superscript';\nimport Typography from '@tiptap/extension-typography';\nimport FontFamily from '@tiptap/extension-font-family';\n\nimport Toolbar from './Toolbar';\nimport AIRewrite from './AIRewrite';\nimport ContextMenu from './ContextMenu';\nimport CommentPanel from './CommentPanel';\nimport type { CommentPanelHandle } from './CommentPanel';\nimport CommentDialog from './CommentDialog';\nimport CommentPopover from './CommentPopover';\nimport SlashMenu from './SlashMenu';\nimport BubbleToolbar from './BubbleToolbar';\nimport BlockHandle from './BlockHandle';\nimport { MarkdownBlockExtension } from './extensions/MarkdownBlock';\nimport { CodeBlockExtension } from './extensions/CodeBlockView';\nimport FontSize from './extensions/FontSize';\nimport { ResizableImage } from './extensions/ResizableImage';\nimport { FloatingDialogExtension } from './extensions/FloatingDialog';\nimport { CommentMark } from './extensions/CommentMark';\nimport type { CommentThread } from './extensions/CommentMark';\nimport type { RichTextEditorProps } from './types';\nimport './styles.css';\n\nfunction generateId() {\n return `c_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\nconst RichTextEditor: React.FC<RichTextEditorProps> = ({\n content,\n value,\n onChange,\n onChangeJSON,\n placeholder = 'Start writing...',\n editable = true,\n aiConfig,\n toolbarConfig,\n minHeight = '200px',\n maxHeight,\n className = '',\n autofocus = false,\n labels,\n editorRef,\n notionMode: initialNotionMode = false,\n showComments: initialShowComments = false,\n commentThreads: externalThreads,\n onCommentThreadsChange,\n currentUser,\n}) => {\n const [formatPainterActive, setFormatPainterActive] = useState(false);\n const [showAIPanel, setShowAIPanel] = useState(false);\n const [aiSelection, setAiSelection] = useState('');\n const [aiSelectionRange, setAiSelectionRange] = useState({ from: 0, to: 0 });\n const [notionMode, setNotionMode] = useState(initialNotionMode);\n const [showComments, setShowComments] = useState(initialShowComments);\n const [internalThreads, setInternalThreads] = useState<CommentThread[]>([]);\n const [commentDialogOpen, setCommentDialogOpen] = useState(false);\n const [commentSelectionRange, setCommentSelectionRange] = useState({ from: 0, to: 0 });\n const commentPanelRef = useRef<CommentPanelHandle>(null);\n\n const threads = externalThreads ?? internalThreads;\n const setThreads = onCommentThreadsChange ?? setInternalThreads;\n\n const editor = useEditor({\n extensions: [\n StarterKit.configure({\n codeBlock: false,\n heading: { levels: [1, 2, 3, 4, 5, 6] },\n // v3 StarterKit bundles these; explicit extensions below stay in charge.\n link: false,\n underline: false,\n }),\n Underline,\n TextStyle,\n Color,\n Highlight.configure({ multicolor: true }),\n TextAlign.configure({\n types: ['heading', 'paragraph'],\n }),\n Link.configure({\n openOnClick: false,\n autolink: true,\n HTMLAttributes: { class: 'rte-link' },\n }),\n ResizableImage.configure({ inline: false, allowBase64: true }),\n Table.configure({ resizable: true }),\n TableRow,\n TableCell,\n TableHeader,\n Placeholder.configure({ placeholder }),\n CodeBlockExtension,\n TaskList,\n TaskItem.configure({ nested: true }),\n Subscript,\n Superscript,\n Typography,\n FontFamily,\n FontSize,\n FloatingDialogExtension,\n MarkdownBlockExtension,\n CommentMark,\n ],\n content: value || content || '',\n editable,\n autofocus: autofocus ? 'end' : false,\n onUpdate: ({ editor: e }) => {\n onChange?.(e.getHTML());\n onChangeJSON?.(e.getJSON() as Record<string, unknown>);\n },\n });\n\n useEffect(() => {\n if (editorRef && editor) {\n editorRef.current = editor;\n }\n }, [editor, editorRef]);\n\n useEffect(() => {\n if (editor && value !== undefined) {\n const currentHTML = editor.getHTML();\n if (value !== currentHTML) {\n editor.commands.setContent(value, { emitUpdate: false });\n }\n }\n }, [editor, value]);\n\n useEffect(() => {\n if (editor) {\n editor.setEditable(editable);\n }\n }, [editor, editable]);\n\n const handleAIClick = useCallback(() => {\n if (!editor) return;\n const { from, to } = editor.state.selection;\n const text = from !== to\n ? editor.state.doc.textBetween(from, to, ' ')\n : '';\n setAiSelection(text);\n setAiSelectionRange({ from, to });\n setShowAIPanel(true);\n }, [editor]);\n\n const requestAddComment = useCallback(() => {\n if (!editor) return;\n const { from, to } = editor.state.selection;\n if (from === to) return;\n setCommentSelectionRange({ from, to });\n setCommentDialogOpen(true);\n }, [editor]);\n\n const handleCommentSubmit = useCallback((text: string) => {\n if (!editor) return;\n const id = generateId();\n const thread: CommentThread = {\n id,\n resolved: false,\n comments: [{\n id: generateId(),\n author: currentUser || 'User',\n text,\n createdAt: Date.now(),\n }],\n createdAt: Date.now(),\n };\n editor.chain()\n .focus()\n .setTextSelection(commentSelectionRange)\n .setComment(id)\n .run();\n setThreads([...threads, thread]);\n setCommentDialogOpen(false);\n }, [editor, commentSelectionRange, threads, setThreads, currentUser]);\n\n const handleScrollToThread = useCallback((id: string) => {\n commentPanelRef.current?.scrollToThread(id);\n }, []);\n\n if (!editor) return null;\n\n const minH = typeof minHeight === 'number' ? `${minHeight}px` : minHeight;\n const maxH = maxHeight ? (typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight) : undefined;\n\n const containerClass = cn(\n 'rte-container',\n formatPainterActive && 'rte-container--format-painter',\n notionMode && 'rte-container--notion',\n className,\n );\n\n const activeCommentCount = threads.filter((t) => !t.resolved).length;\n\n const modeToggle = (\n <div className=\"rte-mode-toggle\">\n <button\n type=\"button\"\n className={`rte-mode-toggle__btn ${!notionMode ? 'rte-mode-toggle__btn--active' : ''}`}\n onClick={() => setNotionMode(false)}\n title=\"Classic mode\"\n >\n Classic\n </button>\n <button\n type=\"button\"\n className={`rte-mode-toggle__btn ${notionMode ? 'rte-mode-toggle__btn--active' : ''}`}\n onClick={() => setNotionMode(true)}\n title=\"Notion-style mode\"\n >\n Notion\n </button>\n <div className=\"rte-toolbar__divider\" style={{ height: 20, margin: '0 4px' }} />\n <button\n type=\"button\"\n className={`rte-toolbar__btn rte-toolbar__comment-btn ${showComments ? 'rte-toolbar__btn--active' : ''}`}\n onClick={() => setShowComments(!showComments)}\n title=\"Toggle comments\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z\" />\n </svg>\n {activeCommentCount > 0 && <span className=\"rte-toolbar__comment-badge\" />}\n </button>\n </div>\n );\n\n return (\n <div className={containerClass} style={{ minHeight: minH, maxHeight: maxH }}>\n {editable && !notionMode && (\n <Toolbar\n editor={editor}\n config={toolbarConfig}\n labels={labels}\n hasAI={!!aiConfig}\n onAIClick={handleAIClick}\n onFormatPainterChange={setFormatPainterActive}\n >\n {modeToggle}\n </Toolbar>\n )}\n\n {editable && notionMode && (\n <div className=\"rte-notion-topbar\">\n {modeToggle}\n </div>\n )}\n\n <div style={{ display: 'flex', flex: 1, overflow: 'hidden' }}>\n <div className=\"rte-editor-wrapper\" style={{ flex: 1 }}>\n <EditorContent editor={editor} className=\"rte-editor-content\" />\n {editable && <ContextMenu editor={editor} labels={labels} onAIClick={aiConfig ? handleAIClick : undefined} />}\n {editable && <SlashMenu editor={editor} />}\n {editable && notionMode && <BubbleToolbar editor={editor} onAddComment={requestAddComment} />}\n {editable && notionMode && <BlockHandle editor={editor} />}\n <CommentPopover\n editor={editor}\n threads={threads}\n onThreadsChange={setThreads}\n showPanel={showComments}\n onOpenPanel={() => setShowComments(true)}\n onScrollToThread={handleScrollToThread}\n currentUser={currentUser}\n />\n </div>\n\n {showComments && (\n <CommentPanel\n ref={commentPanelRef}\n editor={editor}\n threads={threads}\n onThreadsChange={setThreads}\n onRequestAddComment={requestAddComment}\n currentUser={currentUser}\n />\n )}\n </div>\n\n {aiConfig && showAIPanel && (\n <AIRewrite\n editor={editor}\n aiConfig={aiConfig}\n labels={labels}\n initialSelection={aiSelection}\n selectionRange={aiSelectionRange}\n onClose={() => setShowAIPanel(false)}\n />\n )}\n\n <CommentDialog\n open={commentDialogOpen}\n onSubmit={handleCommentSubmit}\n onClose={() => setCommentDialogOpen(false)}\n />\n </div>\n );\n};\n\nexport default RichTextEditor;\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const ResizableImage: import("@tiptap/core").Node<import("@tiptap/extension-image").ImageOptions, any>;
2
+ export default ResizableImage;
@@ -0,0 +1,94 @@
1
+ import { useNodeEditable as e } from "./MarkdownBlock.js";
2
+ import { useCallback as t, useRef as n, useState as r } from "react";
3
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
4
+ import { NodeViewWrapper as o, ReactNodeViewRenderer as s } from "@tiptap/react";
5
+ import c from "@tiptap/extension-image";
6
+ //#region src/components/pro/RichTextEditor/extensions/ResizableImage.tsx
7
+ var l = 40, u = ({ node: s, updateAttributes: c, selected: u, editor: d }) => {
8
+ let f = e(d), p = n(null), [m, h] = r(!1), { src: g, alt: _, title: v, width: y, align: b } = s.attrs, x = t((e) => (t) => {
9
+ if (!f) return;
10
+ t.preventDefault(), t.stopPropagation();
11
+ let n = p.current;
12
+ if (!n) return;
13
+ let r = "touches" in t ? t.touches[0].clientX : t.clientX, i = n.getBoundingClientRect().width, a = n.naturalHeight && n.naturalWidth ? n.naturalHeight / n.naturalWidth : 0, o = e === "nw" || e === "sw" ? -1 : 1, s = n.parentElement?.parentElement?.getBoundingClientRect().width ?? Infinity;
14
+ h(!0);
15
+ let u = (e) => {
16
+ let t = (e - r) * o, n = Math.round(Math.min(s, Math.max(l, i + t)));
17
+ c({
18
+ width: n,
19
+ height: a ? Math.round(n * a) : null
20
+ });
21
+ }, d = (e) => u(e.clientX), m = (e) => {
22
+ e.cancelable && e.preventDefault(), u(e.touches[0].clientX);
23
+ }, g = () => {
24
+ h(!1), window.removeEventListener("mousemove", d), window.removeEventListener("mouseup", g), window.removeEventListener("touchmove", m), window.removeEventListener("touchend", g), window.removeEventListener("touchcancel", g);
25
+ };
26
+ window.addEventListener("mousemove", d), window.addEventListener("mouseup", g), window.addEventListener("touchmove", m, { passive: !1 }), window.addEventListener("touchend", g), window.addEventListener("touchcancel", g);
27
+ }, [f, c]), S = f && (u || m);
28
+ return /* @__PURE__ */ i(o, {
29
+ className: "rte-resizable-image",
30
+ "data-align": b || "left",
31
+ "data-active": S ? "true" : void 0,
32
+ children: /* @__PURE__ */ a("span", {
33
+ className: "rte-resizable-image__frame",
34
+ style: { width: y ? `${y}px` : void 0 },
35
+ children: [/* @__PURE__ */ i("img", {
36
+ ref: p,
37
+ src: g,
38
+ alt: _ || "",
39
+ title: v || void 0,
40
+ draggable: !1,
41
+ style: { width: y ? `${y}px` : void 0 }
42
+ }), S && [
43
+ "nw",
44
+ "ne",
45
+ "sw",
46
+ "se"
47
+ ].map((e) => /* @__PURE__ */ i("span", {
48
+ className: `rte-resizable-image__handle rte-resizable-image__handle--${e}`,
49
+ onMouseDown: x(e),
50
+ onTouchStart: x(e),
51
+ role: "presentation"
52
+ }, e))]
53
+ })
54
+ });
55
+ }, d = c.extend({
56
+ addAttributes() {
57
+ return {
58
+ ...this.parent?.(),
59
+ width: {
60
+ default: null,
61
+ parseHTML: (e) => {
62
+ let t = e.getAttribute("width");
63
+ if (t) return parseInt(t, 10) || null;
64
+ let n = e.style.width;
65
+ return n && parseInt(n, 10) || null;
66
+ },
67
+ renderHTML: (e) => e.width ? {
68
+ width: e.width,
69
+ style: `width: ${e.width}px`
70
+ } : {}
71
+ },
72
+ height: {
73
+ default: null,
74
+ parseHTML: (e) => {
75
+ let t = e.getAttribute("height");
76
+ return t && parseInt(t, 10) || null;
77
+ },
78
+ renderHTML: (e) => e.height ? { height: e.height } : {}
79
+ },
80
+ align: {
81
+ default: "left",
82
+ parseHTML: (e) => e.getAttribute("data-align") || "left",
83
+ renderHTML: (e) => e.align ? { "data-align": e.align } : {}
84
+ }
85
+ };
86
+ },
87
+ addNodeView() {
88
+ return s(u);
89
+ }
90
+ });
91
+ //#endregion
92
+ export { d as ResizableImage, d as default };
93
+
94
+ //# sourceMappingURL=ResizableImage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResizableImage.js","names":[],"sources":["../../../../../src/components/pro/RichTextEditor/extensions/ResizableImage.tsx"],"sourcesContent":["import React, { useCallback, useRef, useState } from 'react';\nimport Image from '@tiptap/extension-image';\nimport { NodeViewWrapper, ReactNodeViewRenderer } from '@tiptap/react';\nimport type { NodeViewProps } from '@tiptap/react';\nimport { useNodeEditable } from './MarkdownBlock';\n\n/**\n * ResizableImage extends the stock Tiptap Image node with:\n * - persisted `width` / `height` / `align` attributes (serialized to HTML)\n * - a React NodeView that renders drag handles on the four corners so users can\n * resize the image by dragging (mouse + touch), keeping the aspect ratio.\n *\n * The public API is unchanged: `editor.chain().setImage({ src })` still works,\n * and existing documents (plain `<img>`) upgrade transparently.\n */\n\nconst MIN_WIDTH = 40;\n\ntype Corner = 'nw' | 'ne' | 'sw' | 'se';\n\nconst ResizableImageNodeView: React.FC<NodeViewProps> = ({ node, updateAttributes, selected, editor }) => {\n const editable = useNodeEditable(editor);\n const imgRef = useRef<HTMLImageElement>(null);\n const [resizing, setResizing] = useState(false);\n\n const { src, alt, title, width, align } = node.attrs as {\n src: string;\n alt?: string;\n title?: string;\n width?: number | null;\n height?: number | null;\n align?: 'left' | 'center' | 'right' | null;\n };\n\n const startResize = useCallback(\n (corner: Corner) => (event: React.MouseEvent | React.TouchEvent) => {\n if (!editable) return;\n event.preventDefault();\n event.stopPropagation();\n\n const img = imgRef.current;\n if (!img) return;\n\n const isTouch = 'touches' in event;\n const startX = isTouch ? event.touches[0].clientX : (event as React.MouseEvent).clientX;\n const startWidth = img.getBoundingClientRect().width;\n const ratio = img.naturalHeight && img.naturalWidth ? img.naturalHeight / img.naturalWidth : 0;\n // Left-side handles grow the image when dragged leftwards.\n const dir = corner === 'nw' || corner === 'sw' ? -1 : 1;\n const maxWidth = img.parentElement?.parentElement?.getBoundingClientRect().width ?? Infinity;\n\n setResizing(true);\n\n const move = (clientX: number) => {\n const delta = (clientX - startX) * dir;\n const next = Math.round(Math.min(maxWidth, Math.max(MIN_WIDTH, startWidth + delta)));\n updateAttributes({ width: next, height: ratio ? Math.round(next * ratio) : null });\n };\n\n const onMouseMove = (e: MouseEvent) => move(e.clientX);\n const onTouchMove = (e: TouchEvent) => {\n if (e.cancelable) e.preventDefault();\n move(e.touches[0].clientX);\n };\n const stop = () => {\n setResizing(false);\n window.removeEventListener('mousemove', onMouseMove);\n window.removeEventListener('mouseup', stop);\n window.removeEventListener('touchmove', onTouchMove);\n window.removeEventListener('touchend', stop);\n window.removeEventListener('touchcancel', stop);\n };\n\n window.addEventListener('mousemove', onMouseMove);\n window.addEventListener('mouseup', stop);\n window.addEventListener('touchmove', onTouchMove, { passive: false });\n window.addEventListener('touchend', stop);\n window.addEventListener('touchcancel', stop);\n },\n [editable, updateAttributes],\n );\n\n const active = editable && (selected || resizing);\n const corners: Corner[] = ['nw', 'ne', 'sw', 'se'];\n\n return (\n <NodeViewWrapper\n className=\"rte-resizable-image\"\n data-align={align || 'left'}\n data-active={active ? 'true' : undefined}\n >\n <span className=\"rte-resizable-image__frame\" style={{ width: width ? `${width}px` : undefined }}>\n <img\n ref={imgRef}\n src={src}\n alt={alt || ''}\n title={title || undefined}\n draggable={false}\n style={{ width: width ? `${width}px` : undefined }}\n />\n {active &&\n corners.map((corner) => (\n <span\n key={corner}\n className={`rte-resizable-image__handle rte-resizable-image__handle--${corner}`}\n onMouseDown={startResize(corner)}\n onTouchStart={startResize(corner)}\n role=\"presentation\"\n />\n ))}\n </span>\n </NodeViewWrapper>\n );\n};\n\nexport const ResizableImage = Image.extend({\n addAttributes() {\n return {\n ...this.parent?.(),\n width: {\n default: null,\n parseHTML: (element) => {\n const attr = element.getAttribute('width');\n if (attr) return parseInt(attr, 10) || null;\n const style = element.style.width;\n return style ? parseInt(style, 10) || null : null;\n },\n renderHTML: (attributes) =>\n attributes.width ? { width: attributes.width, style: `width: ${attributes.width}px` } : {},\n },\n height: {\n default: null,\n parseHTML: (element) => {\n const attr = element.getAttribute('height');\n return attr ? parseInt(attr, 10) || null : null;\n },\n renderHTML: (attributes) => (attributes.height ? { height: attributes.height } : {}),\n },\n align: {\n default: 'left',\n parseHTML: (element) => element.getAttribute('data-align') || 'left',\n renderHTML: (attributes) => (attributes.align ? { 'data-align': attributes.align } : {}),\n },\n };\n },\n addNodeView() {\n return ReactNodeViewRenderer(ResizableImageNodeView);\n },\n});\n\nexport default ResizableImage;\n"],"mappings":"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"}
@@ -10,6 +10,7 @@ export { default as ContextMenu } from './ContextMenu';
10
10
  export { MarkdownBlockExtension } from './extensions/MarkdownBlock';
11
11
  export { CodeBlockExtension } from './extensions/CodeBlockView';
12
12
  export { default as FontSizeExtension } from './extensions/FontSize';
13
+ export { ResizableImage } from './extensions/ResizableImage';
13
14
  export { FloatingDialogExtension, getNextFloatZIndex } from './extensions/FloatingDialog';
14
15
  export { CommentMark } from './extensions/CommentMark';
15
16
  export { default as RichTextToolbar } from './Toolbar';