@firecms/editor 3.0.0-canary.99 → 3.0.0-rc.2

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.
package/dist/index.umd.js CHANGED
@@ -1,314 +1,301 @@
1
1
  (function(global, factory) {
2
- typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("react/jsx-runtime"), require("react"), require("@tiptap/extension-underline"), require("@tiptap/extension-text-style"), require("@tiptap/extension-color"), require("tiptap-markdown"), require("@tiptap/extension-highlight"), require("@tiptap/react"), require("jotai"), require("@radix-ui/react-slot"), require("tunnel-rat"), require("cmdk"), require("@tiptap/starter-kit"), require("@tiptap/extension-horizontal-rule"), require("@tiptap/extension-link"), require("@tiptap/extension-image"), require("@tiptap/extension-placeholder"), require("@tiptap/extension-task-item"), require("@tiptap/extension-task-list"), require("@tiptap/core"), require("@tiptap/suggestion"), require("tippy.js"), require("@firecms/ui"), require("prosemirror-state"), require("@tiptap/pm/view"), require("@tiptap/pm/state")) : typeof define === "function" && define.amd ? define(["exports", "react/jsx-runtime", "react", "@tiptap/extension-underline", "@tiptap/extension-text-style", "@tiptap/extension-color", "tiptap-markdown", "@tiptap/extension-highlight", "@tiptap/react", "jotai", "@radix-ui/react-slot", "tunnel-rat", "cmdk", "@tiptap/starter-kit", "@tiptap/extension-horizontal-rule", "@tiptap/extension-link", "@tiptap/extension-image", "@tiptap/extension-placeholder", "@tiptap/extension-task-item", "@tiptap/extension-task-list", "@tiptap/core", "@tiptap/suggestion", "tippy.js", "@firecms/ui", "prosemirror-state", "@tiptap/pm/view", "@tiptap/pm/state"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["FireCMS Editor"] = {}, global.jsxRuntime, global.React, global.TiptapUnderline, global.TextStyle, global.extensionColor, global.tiptapMarkdown, global.Highlight, global.react, global.jotai, global.reactSlot, global.tunnel, global.cmdk, global.StarterKit, global.HorizontalRule, global.TiptapLink, global.TiptapImage, global.Placeholder, global.extensionTaskItem, global.extensionTaskList, global.core, global.Suggestion, global.tippy, global.ui, global.prosemirrorState, global.view, global.state));
3
- })(this, function(exports2, jsxRuntime, React, TiptapUnderline, TextStyle, extensionColor, tiptapMarkdown, Highlight, react, jotai, reactSlot, tunnel, cmdk, StarterKit, HorizontalRule, TiptapLink, TiptapImage, Placeholder, extensionTaskItem, extensionTaskList, core, Suggestion, tippy, ui, prosemirrorState, view, state) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("react/jsx-runtime"), require("react"), require("@firecms/ui"), require("@tiptap/react"), require("@tiptap/extension-document"), require("tiptap-markdown"), require("@tiptap/extension-underline"), require("@tiptap/extension-heading"), require("@tiptap/extension-text-style"), require("@tiptap/extension-color"), require("@tiptap/extension-highlight"), require("@tiptap/extension-bold"), require("@tiptap/extension-italic"), require("@tiptap/extension-strike"), require("react-compiler-runtime"), require("@tiptap/react/menus"), require("@tiptap/pm/state"), require("@radix-ui/react-slot"), require("@tiptap/starter-kit"), require("@tiptap/extension-horizontal-rule"), require("@tiptap/extension-link"), require("@tiptap/extension-image"), require("@tiptap/extension-placeholder"), require("@tiptap/extension-task-item"), require("@tiptap/extension-task-list"), require("@tiptap/core"), require("@tiptap/pm/view"), require("@tiptap/extension-ordered-list"), require("@tiptap/extension-bullet-list"), require("@tiptap/extension-list-item"), require("@tiptap/extension-code-block"), require("@tiptap/extension-blockquote"), require("@tiptap/extension-code"), require("@tiptap/pm/model"), require("@tiptap/suggestion"), require("@floating-ui/dom")) : typeof define === "function" && define.amd ? define(["exports", "react/jsx-runtime", "react", "@firecms/ui", "@tiptap/react", "@tiptap/extension-document", "tiptap-markdown", "@tiptap/extension-underline", "@tiptap/extension-heading", "@tiptap/extension-text-style", "@tiptap/extension-color", "@tiptap/extension-highlight", "@tiptap/extension-bold", "@tiptap/extension-italic", "@tiptap/extension-strike", "react-compiler-runtime", "@tiptap/react/menus", "@tiptap/pm/state", "@radix-ui/react-slot", "@tiptap/starter-kit", "@tiptap/extension-horizontal-rule", "@tiptap/extension-link", "@tiptap/extension-image", "@tiptap/extension-placeholder", "@tiptap/extension-task-item", "@tiptap/extension-task-list", "@tiptap/core", "@tiptap/pm/view", "@tiptap/extension-ordered-list", "@tiptap/extension-bullet-list", "@tiptap/extension-list-item", "@tiptap/extension-code-block", "@tiptap/extension-blockquote", "@tiptap/extension-code", "@tiptap/pm/model", "@tiptap/suggestion", "@floating-ui/dom"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["FireCMS Editor"] = {}, global.jsxRuntime, global.React, global.ui, global.react, global.Document, global.tiptapMarkdown, global.Underline, global.Heading, global.extensionTextStyle, global.Color, global.Highlight, global.Bold, global.Italic, global.Strike, global.reactCompilerRuntime, global.menus, global.state, global.reactSlot, global.StarterKit, global.HorizontalRule, global.TiptapLink, global.TiptapImage, global.Placeholder, global.extensionTaskItem, global.extensionTaskList, global.core, global.view, global.OrderedList, global.BulletList, global.ListItem, global.CodeBlock, global.Blockquote, global.Code, global.model, global.Suggestion, global.dom));
3
+ })(this, (function(exports2, jsxRuntime, React, ui, react, Document, tiptapMarkdown, Underline, Heading, extensionTextStyle, Color, Highlight, Bold, Italic, Strike, reactCompilerRuntime, menus, state, reactSlot, StarterKit, HorizontalRule, TiptapLink, TiptapImage, Placeholder, extensionTaskItem, extensionTaskList, core, view, OrderedList, BulletList, ListItem, CodeBlock, Blockquote, Code, model, Suggestion, dom) {
4
4
  "use strict";
5
- const editorStore = jotai.createStore();
6
- const EditorRoot = ({ children }) => {
7
- return /* @__PURE__ */ jsxRuntime.jsx(jotai.Provider, { store: editorStore, children });
8
- };
9
- const EditorBubble = React.forwardRef(
10
- ({ children, tippyOptions, ...rest }, ref) => {
11
- const { editor } = react.useCurrentEditor();
12
- const instanceRef = React.useRef(null);
13
- React.useEffect(() => {
14
- if (!instanceRef.current || !tippyOptions?.placement) return;
15
- instanceRef.current.setProps({ placement: tippyOptions.placement });
16
- instanceRef.current.popperInstance?.update();
17
- }, [tippyOptions?.placement]);
18
- const bubbleMenuProps = React.useMemo(() => {
19
- const shouldShow = ({ editor: editor2, state: state2 }) => {
20
- const { selection } = state2;
21
- const { empty } = selection;
22
- if (editor2.isActive("image") || empty || react.isNodeSelection(selection)) {
23
- return false;
24
- }
25
- return true;
26
- };
27
- return {
28
- shouldShow,
29
- tippyOptions: {
30
- onCreate: (val) => {
31
- instanceRef.current = val;
32
- },
33
- moveTransition: "transform 0.15s ease-out",
34
- ...tippyOptions
35
- },
36
- ...rest
37
- };
38
- }, [rest, tippyOptions]);
39
- if (!editor) return null;
40
- return (
41
- //We need to add this because of https://github.com/ueberdosis/tiptap/issues/2658
42
- /* @__PURE__ */ jsxRuntime.jsx("div", { ref, children: /* @__PURE__ */ jsxRuntime.jsx(react.BubbleMenu, { editor, ...bubbleMenuProps, children }) })
43
- );
44
- }
45
- );
46
- const EditorBubbleItem = React.forwardRef(({ children, asChild, onSelect, ...rest }, ref) => {
47
- const { editor } = react.useCurrentEditor();
48
- const Comp = asChild ? reactSlot.Slot : "div";
49
- if (!editor) return null;
50
- return /* @__PURE__ */ jsxRuntime.jsx(Comp, { ref, ...rest, onClick: () => onSelect?.(editor), children });
51
- });
52
- EditorBubbleItem.displayName = "EditorBubbleItem";
53
- const t = tunnel();
54
- const queryAtom = jotai.atom("");
55
- const rangeAtom = jotai.atom(null);
56
- const EditorCommandOut = ({
57
- query,
58
- range
59
- }) => {
60
- const setQuery = jotai.useSetAtom(queryAtom, { store: editorStore });
61
- const setRange = jotai.useSetAtom(rangeAtom, { store: editorStore });
62
- React.useEffect(() => {
63
- setQuery(query);
64
- }, [query, setQuery]);
65
- React.useEffect(() => {
66
- setRange(range);
67
- }, [range, setRange]);
68
- React.useEffect(() => {
69
- const navigationKeys = ["ArrowUp", "ArrowDown", "Enter"];
70
- const onKeyDown = (e) => {
71
- if (navigationKeys.includes(e.key)) {
72
- e.preventDefault();
73
- const commandRef = document.querySelector("#slash-command");
74
- if (commandRef)
75
- commandRef.dispatchEvent(
76
- new KeyboardEvent("keydown", { key: e.key, cancelable: true, bubbles: true })
77
- );
78
- }
79
- };
80
- document.addEventListener("keydown", onKeyDown);
81
- return () => {
82
- document.removeEventListener("keydown", onKeyDown);
5
+ const EditorBubble = React.forwardRef((t0, ref) => {
6
+ const $ = reactCompilerRuntime.c(18);
7
+ let children;
8
+ let options;
9
+ let rest;
10
+ let tippyOptions;
11
+ if ($[0] !== t0) {
12
+ ({
13
+ children,
14
+ tippyOptions,
15
+ options,
16
+ ...rest
17
+ } = t0);
18
+ $[0] = t0;
19
+ $[1] = children;
20
+ $[2] = options;
21
+ $[3] = rest;
22
+ $[4] = tippyOptions;
23
+ } else {
24
+ children = $[1];
25
+ options = $[2];
26
+ rest = $[3];
27
+ tippyOptions = $[4];
28
+ }
29
+ const {
30
+ editor
31
+ } = react.useCurrentEditor();
32
+ tippyOptions?.placement;
33
+ let t1;
34
+ const shouldShow = _temp$1;
35
+ const t2 = tippyOptions?.placement ?? options?.placement;
36
+ let t3;
37
+ if ($[5] !== options || $[6] !== t2) {
38
+ t3 = {
39
+ ...options,
40
+ placement: t2
83
41
  };
84
- }, []);
85
- return /* @__PURE__ */ jsxRuntime.jsx(t.Out, {});
86
- };
87
- const EditorCommand = React.forwardRef(
88
- ({ children, className, ...rest }, ref) => {
89
- const commandListRef = React.useRef(null);
90
- const [query, setQuery] = jotai.useAtom(queryAtom);
91
- return /* @__PURE__ */ jsxRuntime.jsx(t.In, { children: /* @__PURE__ */ jsxRuntime.jsxs(
92
- cmdk.Command,
93
- {
94
- ref,
95
- onKeyDown: (e) => {
96
- e.stopPropagation();
97
- },
98
- id: "slash-command",
99
- className,
100
- ...rest,
101
- children: [
102
- /* @__PURE__ */ jsxRuntime.jsx(cmdk.Command.Input, { value: query, onValueChange: setQuery, style: { display: "none" } }),
103
- /* @__PURE__ */ jsxRuntime.jsx(cmdk.Command.List, { ref: commandListRef, children })
104
- ]
105
- }
106
- ) });
107
- }
108
- );
109
- const EditorCommandItem = React.forwardRef(({ children, onCommand, ...rest }, ref) => {
110
- const { editor } = react.useCurrentEditor();
111
- const range = jotai.useAtomValue(rangeAtom);
112
- if (!editor || !range) return null;
113
- return /* @__PURE__ */ jsxRuntime.jsx(cmdk.CommandItem, { ref, ...rest, onSelect: () => onCommand({ editor, range }), children });
114
- });
115
- EditorCommandItem.displayName = "EditorCommandItem";
116
- const EditorCommandEmpty = cmdk.CommandEmpty;
117
- const Command = core.Extension.create({
118
- name: "slash-command",
119
- addOptions() {
120
- return {
121
- suggestion: {
122
- char: "/",
123
- command: ({ editor, range, props }) => {
124
- props.command({ editor, range });
125
- }
126
- }
42
+ $[5] = options;
43
+ $[6] = t2;
44
+ $[7] = t3;
45
+ } else {
46
+ t3 = $[7];
47
+ }
48
+ const mergedOptions = t3;
49
+ let t4;
50
+ if ($[8] !== mergedOptions || $[9] !== rest) {
51
+ t4 = {
52
+ shouldShow,
53
+ options: mergedOptions,
54
+ ...rest
127
55
  };
128
- },
129
- addProseMirrorPlugins() {
130
- return [
131
- Suggestion({
132
- editor: this.editor,
133
- ...this.options.suggestion
134
- })
135
- ];
56
+ $[8] = mergedOptions;
57
+ $[9] = rest;
58
+ $[10] = t4;
59
+ } else {
60
+ t4 = $[10];
136
61
  }
62
+ t1 = t4;
63
+ const bubbleMenuProps = t1;
64
+ if (!editor) {
65
+ return null;
66
+ }
67
+ let t5;
68
+ if ($[11] !== bubbleMenuProps || $[12] !== children || $[13] !== editor) {
69
+ t5 = /* @__PURE__ */ jsxRuntime.jsx(menus.BubbleMenu, { editor, ...bubbleMenuProps, children });
70
+ $[11] = bubbleMenuProps;
71
+ $[12] = children;
72
+ $[13] = editor;
73
+ $[14] = t5;
74
+ } else {
75
+ t5 = $[14];
76
+ }
77
+ let t6;
78
+ if ($[15] !== ref || $[16] !== t5) {
79
+ t6 = /* @__PURE__ */ jsxRuntime.jsx("div", { ref, children: t5 });
80
+ $[15] = ref;
81
+ $[16] = t5;
82
+ $[17] = t6;
83
+ } else {
84
+ t6 = $[17];
85
+ }
86
+ return t6;
137
87
  });
138
- const renderItems = () => {
139
- let component = null;
140
- let popup = null;
141
- return {
142
- onStart: (props) => {
143
- component = new react.ReactRenderer(EditorCommandOut, {
144
- props,
145
- editor: props.editor
146
- });
147
- popup = tippy("body", {
148
- getReferenceClientRect: props.clientRect,
149
- appendTo: () => document.body,
150
- content: component.element,
151
- showOnCreate: true,
152
- interactive: true,
153
- trigger: "manual",
154
- placement: "bottom-start"
155
- });
156
- },
157
- onUpdate: (props) => {
158
- component?.updateProps(props);
159
- popup && popup[0].setProps({
160
- getReferenceClientRect: props.clientRect
161
- });
162
- },
163
- onKeyDown: (props) => {
164
- if (props.event.key === "Escape") {
165
- popup?.[0].hide();
166
- return true;
167
- }
168
- return component?.ref?.onKeyDown(props);
169
- },
170
- onExit: () => {
171
- popup?.[0].destroy();
172
- component?.destroy();
173
- }
174
- };
175
- };
176
- const createSuggestionItems = (items2) => items2;
177
- const PlaceholderExtension = Placeholder.configure({
178
- placeholder: ({ node }) => {
179
- if (node.type.name === "heading") {
180
- return `Heading ${node.attrs.level}`;
181
- }
182
- return "Press '/' for commands";
183
- },
184
- includeChildren: true
185
- });
186
- const Horizontal = HorizontalRule.extend({
187
- addInputRules() {
188
- return [
189
- new core.InputRule({
190
- find: /^(?:---|—-|___\s|\*\*\*\s)$/,
191
- handler: ({ state: state2, range }) => {
192
- const attributes = {};
193
- const { tr } = state2;
194
- const start = range.from;
195
- const end = range.to;
196
- tr.insert(start - 1, this.type.create(attributes)).delete(
197
- tr.mapping.map(start),
198
- tr.mapping.map(end)
199
- );
200
- }
201
- })
202
- ];
88
+ function _temp$1(t0) {
89
+ const {
90
+ editor: editor_0,
91
+ state: state$1
92
+ } = t0;
93
+ const {
94
+ selection
95
+ } = state$1;
96
+ const {
97
+ empty
98
+ } = selection;
99
+ if (editor_0.isActive("image") || empty || selection instanceof state.NodeSelection) {
100
+ return false;
101
+ }
102
+ return true;
103
+ }
104
+ const EditorBubbleItem = React.forwardRef((t0, ref) => {
105
+ const $ = reactCompilerRuntime.c(14);
106
+ let asChild;
107
+ let children;
108
+ let onSelect;
109
+ let rest;
110
+ if ($[0] !== t0) {
111
+ ({
112
+ children,
113
+ asChild,
114
+ onSelect,
115
+ ...rest
116
+ } = t0);
117
+ $[0] = t0;
118
+ $[1] = asChild;
119
+ $[2] = children;
120
+ $[3] = onSelect;
121
+ $[4] = rest;
122
+ } else {
123
+ asChild = $[1];
124
+ children = $[2];
125
+ onSelect = $[3];
126
+ rest = $[4];
127
+ }
128
+ const {
129
+ editor
130
+ } = react.useCurrentEditor();
131
+ const Comp = asChild ? reactSlot.Slot : "div";
132
+ if (!editor) {
133
+ return null;
134
+ }
135
+ let t1;
136
+ if ($[5] !== editor || $[6] !== onSelect) {
137
+ t1 = () => onSelect?.(editor);
138
+ $[5] = editor;
139
+ $[6] = onSelect;
140
+ $[7] = t1;
141
+ } else {
142
+ t1 = $[7];
203
143
  }
144
+ let t2;
145
+ if ($[8] !== Comp || $[9] !== children || $[10] !== ref || $[11] !== rest || $[12] !== t1) {
146
+ t2 = /* @__PURE__ */ jsxRuntime.jsx(Comp, { ref, ...rest, onClick: t1, children });
147
+ $[8] = Comp;
148
+ $[9] = children;
149
+ $[10] = ref;
150
+ $[11] = rest;
151
+ $[12] = t1;
152
+ $[13] = t2;
153
+ } else {
154
+ t2 = $[13];
155
+ }
156
+ return t2;
204
157
  });
205
- const items = [
206
- {
207
- name: "Text",
208
- icon: ui.TextFieldsIcon,
209
- command: (editor) => editor?.chain().focus().toggleNode("paragraph", "paragraph").run(),
210
- // I feel like there has to be a more efficient way to do this – feel free to PR if you know how!
211
- isActive: (editor) => (editor?.isActive("paragraph") && !editor?.isActive("bulletList") && !editor?.isActive("orderedList")) ?? false
212
- },
213
- {
214
- name: "Heading 1",
215
- icon: ui.LooksOneIcon,
216
- command: (editor) => editor?.chain().focus().toggleHeading({ level: 1 }).run(),
217
- isActive: (editor) => editor?.isActive("heading", { level: 1 }) ?? false
218
- },
219
- {
220
- name: "Heading 2",
221
- icon: ui.LooksTwoIcon,
222
- command: (editor) => editor?.chain().focus().toggleHeading({ level: 2 }).run(),
223
- isActive: (editor) => editor?.isActive("heading", { level: 2 }) ?? false
224
- },
225
- {
226
- name: "Heading 3",
227
- icon: ui.Looks3Icon,
228
- command: (editor) => editor?.chain().focus().toggleHeading({ level: 3 }).run(),
229
- isActive: (editor) => editor?.isActive("heading", { level: 3 }) ?? false
230
- },
231
- {
232
- name: "To-do List",
233
- icon: ui.CheckBoxIcon,
234
- command: (editor) => editor?.chain().focus().toggleTaskList().run(),
235
- isActive: (editor) => editor?.isActive("taskItem") ?? false
236
- },
237
- {
238
- name: "Bullet List",
239
- icon: ui.FormatListBulletedIcon,
240
- command: (editor) => editor?.chain().focus().toggleBulletList().run(),
241
- isActive: (editor) => editor?.isActive("bulletList") ?? false
242
- },
243
- {
244
- name: "Numbered List",
245
- icon: ui.FormatListNumberedIcon,
246
- command: (editor) => editor?.chain().focus().toggleOrderedList().run(),
247
- isActive: (editor) => editor?.isActive("orderedList") ?? false
248
- },
249
- {
250
- name: "Quote",
251
- icon: ui.FormatQuoteIcon,
252
- command: (editor) => editor?.chain().focus().toggleNode("paragraph", "paragraph").toggleBlockquote().run(),
253
- isActive: (editor) => editor?.isActive("blockquote") ?? false
254
- },
255
- {
256
- name: "Code",
257
- icon: ui.CodeIcon,
258
- command: (editor) => editor?.chain().focus().toggleCodeBlock().run(),
259
- isActive: (editor) => editor?.isActive("codeBlock") ?? false
260
- }
261
- ];
262
- const NodeSelector = ({
263
- open,
264
- onOpenChange
265
- }) => {
266
- const { editor } = react.useCurrentEditor();
267
- if (!editor) return null;
268
- const activeItem = items.filter((item) => item.isActive(editor)).pop() ?? {
269
- name: "Multiple"
270
- };
271
- return /* @__PURE__ */ jsxRuntime.jsx(
272
- ui.Popover,
273
- {
274
- sideOffset: 5,
275
- align: "start",
276
- className: "w-48 p-1",
277
- trigger: /* @__PURE__ */ jsxRuntime.jsxs(
278
- ui.Button,
279
- {
280
- variant: "text",
281
- className: "gap-2 rounded-none",
282
- color: "text",
283
- children: [
284
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "whitespace-nowrap text-sm", children: activeItem.name }),
285
- /* @__PURE__ */ jsxRuntime.jsx(ui.ExpandMoreIcon, { size: "small" })
286
- ]
287
- }
288
- ),
289
- modal: true,
290
- open,
291
- onOpenChange,
292
- children: items.map((item, index) => /* @__PURE__ */ jsxRuntime.jsxs(
293
- EditorBubbleItem,
294
- {
295
- onSelect: (editor2) => {
296
- item.command(editor2);
297
- onOpenChange(false);
298
- },
299
- className: "flex cursor-pointer items-center justify-between rounded px-2 py-1 text-sm hover:bg-blue-50 hover:dark:bg-gray-700 text-gray-900 dark:text-white",
300
- children: [
301
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2", children: [
302
- /* @__PURE__ */ jsxRuntime.jsx(item.icon, { size: "smallest" }),
303
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: item.name })
304
- ] }),
305
- activeItem.name === item.name && /* @__PURE__ */ jsxRuntime.jsx(ui.CheckIcon, { size: "smallest" })
306
- ]
307
- },
308
- index
309
- ))
310
- }
311
- );
158
+ EditorBubbleItem.displayName = "EditorBubbleItem";
159
+ const items = [{
160
+ name: "Text",
161
+ icon: ui.TextFieldsIcon,
162
+ command: (editor) => editor?.chain().focus().toggleNode("paragraph", "paragraph").run(),
163
+ // I feel like there has to be a more efficient way to do this – feel free to PR if you know how!
164
+ isActive: (editor) => (editor?.isActive("paragraph") && !editor?.isActive("bulletList") && !editor?.isActive("orderedList")) ?? false
165
+ }, {
166
+ name: "Heading 1",
167
+ icon: ui.LooksOneIcon,
168
+ command: (editor) => editor?.chain().focus().toggleHeading({
169
+ level: 1
170
+ }).run(),
171
+ isActive: (editor) => editor?.isActive("heading", {
172
+ level: 1
173
+ }) ?? false
174
+ }, {
175
+ name: "Heading 2",
176
+ icon: ui.LooksTwoIcon,
177
+ command: (editor) => editor?.chain().focus().toggleHeading({
178
+ level: 2
179
+ }).run(),
180
+ isActive: (editor) => editor?.isActive("heading", {
181
+ level: 2
182
+ }) ?? false
183
+ }, {
184
+ name: "Heading 3",
185
+ icon: ui.Looks3Icon,
186
+ command: (editor) => editor?.chain().focus().toggleHeading({
187
+ level: 3
188
+ }).run(),
189
+ isActive: (editor) => editor?.isActive("heading", {
190
+ level: 3
191
+ }) ?? false
192
+ }, {
193
+ name: "To-do List",
194
+ icon: ui.CheckBoxIcon,
195
+ command: (editor) => editor?.chain().focus().toggleTaskList().run(),
196
+ isActive: (editor) => editor?.isActive("taskItem") ?? false
197
+ }, {
198
+ name: "Bullet List",
199
+ icon: ui.FormatListBulletedIcon,
200
+ command: (editor) => editor?.chain().focus().toggleBulletList().run(),
201
+ isActive: (editor) => editor?.isActive("bulletList") ?? false
202
+ }, {
203
+ name: "Numbered List",
204
+ icon: ui.FormatListNumberedIcon,
205
+ command: (editor) => editor?.chain().focus().toggleOrderedList().run(),
206
+ isActive: (editor) => editor?.isActive("orderedList") ?? false
207
+ }, {
208
+ name: "Quote",
209
+ icon: ui.FormatQuoteIcon,
210
+ command: (editor) => editor?.chain().focus().toggleNode("paragraph", "paragraph").toggleBlockquote().run(),
211
+ isActive: (editor) => editor?.isActive("blockquote") ?? false
212
+ }, {
213
+ name: "Code",
214
+ icon: ui.CodeIcon,
215
+ command: (editor) => editor?.chain().focus().toggleCodeBlock().run(),
216
+ isActive: (editor) => editor?.isActive("codeBlock") ?? false
217
+ }];
218
+ const NodeSelector = (t0) => {
219
+ const $ = reactCompilerRuntime.c(16);
220
+ const {
221
+ open,
222
+ onOpenChange,
223
+ portalContainer
224
+ } = t0;
225
+ const {
226
+ editor
227
+ } = react.useCurrentEditor();
228
+ if (!editor) {
229
+ return null;
230
+ }
231
+ let t1;
232
+ if ($[0] !== editor) {
233
+ t1 = items.filter((item) => item.isActive(editor)).pop() ?? {
234
+ name: "Multiple"
235
+ };
236
+ $[0] = editor;
237
+ $[1] = t1;
238
+ } else {
239
+ t1 = $[1];
240
+ }
241
+ const activeItem = t1;
242
+ let t2;
243
+ if ($[2] !== activeItem.name) {
244
+ t2 = /* @__PURE__ */ jsxRuntime.jsx("span", { className: "whitespace-nowrap text-sm", children: activeItem.name });
245
+ $[2] = activeItem.name;
246
+ $[3] = t2;
247
+ } else {
248
+ t2 = $[3];
249
+ }
250
+ let t3;
251
+ if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
252
+ t3 = /* @__PURE__ */ jsxRuntime.jsx(ui.KeyboardArrowDownIcon, { size: "small" });
253
+ $[4] = t3;
254
+ } else {
255
+ t3 = $[4];
256
+ }
257
+ let t4;
258
+ if ($[5] !== t2) {
259
+ t4 = /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: "text", className: "gap-2 rounded-none", color: "text", children: [
260
+ t2,
261
+ t3
262
+ ] });
263
+ $[5] = t2;
264
+ $[6] = t4;
265
+ } else {
266
+ t4 = $[6];
267
+ }
268
+ let t5;
269
+ if ($[7] !== activeItem.name || $[8] !== onOpenChange) {
270
+ t5 = items.map((item_0, index) => /* @__PURE__ */ jsxRuntime.jsxs(EditorBubbleItem, { onSelect: (editor_0) => {
271
+ item_0.command(editor_0);
272
+ onOpenChange(false);
273
+ }, className: "flex cursor-pointer items-center justify-between rounded px-2 py-1 text-sm hover:bg-blue-50 hover:dark:bg-surface-700 text-surface-900 dark:text-white", children: [
274
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2", children: [
275
+ /* @__PURE__ */ jsxRuntime.jsx(item_0.icon, { size: "smallest" }),
276
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: item_0.name })
277
+ ] }),
278
+ activeItem.name === item_0.name && /* @__PURE__ */ jsxRuntime.jsx(ui.CheckIcon, { size: "smallest" })
279
+ ] }, index));
280
+ $[7] = activeItem.name;
281
+ $[8] = onOpenChange;
282
+ $[9] = t5;
283
+ } else {
284
+ t5 = $[9];
285
+ }
286
+ let t6;
287
+ if ($[10] !== onOpenChange || $[11] !== open || $[12] !== portalContainer || $[13] !== t4 || $[14] !== t5) {
288
+ t6 = /* @__PURE__ */ jsxRuntime.jsx(ui.Popover, { sideOffset: 5, align: "start", portalContainer, className: "w-48 p-1", trigger: t4, modal: true, open, onOpenChange, children: t5 });
289
+ $[10] = onOpenChange;
290
+ $[11] = open;
291
+ $[12] = portalContainer;
292
+ $[13] = t4;
293
+ $[14] = t5;
294
+ $[15] = t6;
295
+ } else {
296
+ t6 = $[15];
297
+ }
298
+ return t6;
312
299
  };
313
300
  function isValidUrl(url) {
314
301
  try {
@@ -329,162 +316,201 @@
329
316
  return null;
330
317
  }
331
318
  }
332
- const LinkSelector = ({
333
- open,
334
- onOpenChange
335
- }) => {
319
+ const LinkSelector = (t0) => {
320
+ const $ = reactCompilerRuntime.c(24);
321
+ const {
322
+ open,
323
+ onOpenChange
324
+ } = t0;
336
325
  const inputRef = React.useRef(null);
337
- const { editor } = react.useCurrentEditor();
338
- React.useEffect(() => {
339
- inputRef.current && inputRef.current?.focus();
340
- });
341
- if (!editor) return null;
342
- return /* @__PURE__ */ jsxRuntime.jsx(
343
- ui.Popover,
344
- {
345
- modal: true,
346
- open,
347
- onOpenChange,
348
- trigger: /* @__PURE__ */ jsxRuntime.jsx(
349
- ui.Button,
350
- {
351
- variant: "text",
352
- className: "gap-2 rounded-none",
353
- color: "text",
354
- children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: ui.cls("underline decoration-stone-400 underline-offset-4", {
355
- "text-blue-500": editor.isActive("link")
356
- }), children: "Link" })
357
- }
358
- ),
359
- children: /* @__PURE__ */ jsxRuntime.jsxs(
360
- "form",
361
- {
362
- onSubmit: (e) => {
363
- const target = e.currentTarget;
364
- e.preventDefault();
365
- const input = target[0];
366
- const url = getUrlFromString(input.value);
367
- url && editor.chain().focus().setLink({ href: url }).run();
368
- },
369
- className: "flex p-1",
370
- children: [
371
- /* @__PURE__ */ jsxRuntime.jsx(
372
- "input",
373
- {
374
- ref: inputRef,
375
- autoFocus: open,
376
- placeholder: "Paste a link",
377
- defaultValue: editor.getAttributes("link").href || "",
378
- className: "text-gray-900 dark:text-white flex-grow bg-transparent p-1 text-sm outline-none"
379
- }
380
- ),
381
- editor.getAttributes("link").href ? /* @__PURE__ */ jsxRuntime.jsx(
382
- ui.Button,
383
- {
384
- size: "small",
385
- variant: "text",
386
- type: "button",
387
- color: "text",
388
- className: "flex items-center",
389
- onClick: () => {
390
- editor.chain().focus().unsetLink().run();
391
- },
392
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.DeleteIcon, { size: "small" })
393
- }
394
- ) : /* @__PURE__ */ jsxRuntime.jsx(
395
- ui.Button,
396
- {
397
- size: "small",
398
- variant: "text",
399
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.CheckIcon, { size: "small" })
400
- }
401
- )
402
- ]
403
- }
404
- )
405
- }
406
- );
326
+ const {
327
+ editor
328
+ } = react.useCurrentEditor();
329
+ let t1;
330
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
331
+ t1 = () => {
332
+ inputRef.current && inputRef.current?.focus();
333
+ };
334
+ $[0] = t1;
335
+ } else {
336
+ t1 = $[0];
337
+ }
338
+ React.useEffect(t1);
339
+ if (!editor) {
340
+ return null;
341
+ }
342
+ let t2;
343
+ if ($[1] !== editor) {
344
+ t2 = ui.cls("underline decoration-stone-400 underline-offset-4", {
345
+ "text-blue-500": editor.isActive("link")
346
+ });
347
+ $[1] = editor;
348
+ $[2] = t2;
349
+ } else {
350
+ t2 = $[2];
351
+ }
352
+ let t3;
353
+ if ($[3] !== t2) {
354
+ t3 = /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "text", className: "gap-2 rounded-none", color: "text", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: t2, children: "Link" }) });
355
+ $[3] = t2;
356
+ $[4] = t3;
357
+ } else {
358
+ t3 = $[4];
359
+ }
360
+ let t4;
361
+ if ($[5] !== editor) {
362
+ t4 = (e) => {
363
+ const target = e.currentTarget;
364
+ e.preventDefault();
365
+ const input = target[0];
366
+ const url = getUrlFromString(input.value);
367
+ url && editor.chain().focus().setLink({
368
+ href: url
369
+ }).run();
370
+ };
371
+ $[5] = editor;
372
+ $[6] = t4;
373
+ } else {
374
+ t4 = $[6];
375
+ }
376
+ let t5;
377
+ if ($[7] !== editor) {
378
+ t5 = editor.getAttributes("link").href || "";
379
+ $[7] = editor;
380
+ $[8] = t5;
381
+ } else {
382
+ t5 = $[8];
383
+ }
384
+ let t6;
385
+ if ($[9] === Symbol.for("react.memo_cache_sentinel")) {
386
+ t6 = ui.cls("text-surface-900 dark:text-white flex-grow bg-transparent p-1 text-sm outline-none", ui.focusedDisabled);
387
+ $[9] = t6;
388
+ } else {
389
+ t6 = $[9];
390
+ }
391
+ let t7;
392
+ if ($[10] !== open || $[11] !== t5) {
393
+ t7 = /* @__PURE__ */ jsxRuntime.jsx("input", { ref: inputRef, autoFocus: open, placeholder: "Paste a link", defaultValue: t5, className: t6 });
394
+ $[10] = open;
395
+ $[11] = t5;
396
+ $[12] = t7;
397
+ } else {
398
+ t7 = $[12];
399
+ }
400
+ let t8;
401
+ if ($[13] !== editor) {
402
+ t8 = editor.getAttributes("link").href ? /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "text", type: "button", color: "text", className: "flex items-center", onClick: () => {
403
+ editor.chain().focus().unsetLink().run();
404
+ }, children: /* @__PURE__ */ jsxRuntime.jsx(ui.DeleteIcon, { size: "small" }) }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "text", children: /* @__PURE__ */ jsxRuntime.jsx(ui.CheckIcon, { size: "small" }) });
405
+ $[13] = editor;
406
+ $[14] = t8;
407
+ } else {
408
+ t8 = $[14];
409
+ }
410
+ let t9;
411
+ if ($[15] !== t4 || $[16] !== t7 || $[17] !== t8) {
412
+ t9 = /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: t4, className: "flex p-1", children: [
413
+ t7,
414
+ t8
415
+ ] });
416
+ $[15] = t4;
417
+ $[16] = t7;
418
+ $[17] = t8;
419
+ $[18] = t9;
420
+ } else {
421
+ t9 = $[18];
422
+ }
423
+ let t10;
424
+ if ($[19] !== onOpenChange || $[20] !== open || $[21] !== t3 || $[22] !== t9) {
425
+ t10 = /* @__PURE__ */ jsxRuntime.jsx(ui.Popover, { modal: true, open, onOpenChange, trigger: t3, children: t9 });
426
+ $[19] = onOpenChange;
427
+ $[20] = open;
428
+ $[21] = t3;
429
+ $[22] = t9;
430
+ $[23] = t10;
431
+ } else {
432
+ t10 = $[23];
433
+ }
434
+ return t10;
407
435
  };
408
436
  const TextButtons = () => {
409
- const { editor } = react.useCurrentEditor();
410
- if (!editor) return null;
411
- const items2 = [
412
- {
437
+ const $ = reactCompilerRuntime.c(2);
438
+ const {
439
+ editor
440
+ } = react.useCurrentEditor();
441
+ if (!editor) {
442
+ return null;
443
+ }
444
+ let t0;
445
+ if ($[0] !== editor) {
446
+ const items2 = [{
413
447
  name: "bold",
414
- isActive: (editor2) => editor2?.isActive("bold") ?? false,
415
- command: (editor2) => editor2?.chain().focus().toggleBold().run(),
448
+ isActive: _temp,
449
+ command: _temp2,
416
450
  icon: ui.FormatBoldIcon
417
- },
418
- {
451
+ }, {
419
452
  name: "italic",
420
- isActive: (editor2) => editor2?.isActive("italic") ?? false,
421
- command: (editor2) => editor2?.chain().focus().toggleItalic().run(),
453
+ isActive: _temp3,
454
+ command: _temp4,
422
455
  icon: ui.FormatItalicIcon
423
- },
424
- {
456
+ }, {
425
457
  name: "underline",
426
- isActive: (editor2) => editor2?.isActive("underline") ?? false,
427
- command: (editor2) => editor2?.chain().focus().toggleUnderline().run(),
458
+ isActive: _temp5,
459
+ command: _temp6,
428
460
  icon: ui.FormatUnderlinedIcon
429
- },
430
- {
461
+ }, {
431
462
  name: "strike",
432
- isActive: (editor2) => editor2?.isActive("strike") ?? false,
433
- command: (editor2) => editor2?.chain().focus().toggleStrike().run(),
463
+ isActive: _temp7,
464
+ command: _temp8,
434
465
  icon: ui.FormatStrikethroughIcon
435
- },
436
- {
466
+ }, {
437
467
  name: "code",
438
- isActive: (editor2) => editor2?.isActive("code") ?? false,
439
- command: (editor2) => editor2?.chain().focus().toggleCode().run(),
468
+ isActive: _temp9,
469
+ command: _temp0,
440
470
  icon: ui.CodeIcon
441
- }
442
- ];
443
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex", children: items2.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
444
- EditorBubbleItem,
445
- {
446
- onSelect: (editor2) => {
447
- item.command(editor2);
448
- },
449
- children: /* @__PURE__ */ jsxRuntime.jsx(
450
- ui.Button,
451
- {
452
- size: "small",
453
- color: "text",
454
- className: "gap-2 rounded-none h-full",
455
- variant: "text",
456
- children: /* @__PURE__ */ jsxRuntime.jsx(
457
- item.icon,
458
- {
459
- className: ui.cls({
460
- "text-inherit": !item.isActive(editor),
461
- "text-blue-500": item.isActive(editor)
462
- })
463
- }
464
- )
465
- }
466
- )
467
- },
468
- index
469
- )) });
471
+ }];
472
+ t0 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex", children: items2.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(EditorBubbleItem, { onSelect: (editor_10) => {
473
+ item.command(editor_10);
474
+ }, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", color: "text", className: "gap-2 rounded-none h-full", variant: "text", children: /* @__PURE__ */ jsxRuntime.jsx(item.icon, { className: ui.cls({
475
+ "text-inherit": !item.isActive(editor),
476
+ "text-blue-500": item.isActive(editor)
477
+ }) }) }) }, index)) });
478
+ $[0] = editor;
479
+ $[1] = t0;
480
+ } else {
481
+ t0 = $[1];
482
+ }
483
+ return t0;
470
484
  };
471
- function useDebouncedCallback(value, callback, immediate, timeoutMs = 300) {
472
- const pendingUpdate = React.useRef(false);
473
- const performUpdate = () => {
474
- callback();
475
- pendingUpdate.current = false;
476
- };
477
- const handlerRef = React.useRef(void 0);
478
- React.useEffect(
479
- () => {
480
- pendingUpdate.current = true;
481
- clearTimeout(handlerRef.current);
482
- handlerRef.current = setTimeout(performUpdate, timeoutMs);
483
- return () => {
484
- };
485
- },
486
- [immediate, value]
487
- );
485
+ function _temp(editor_0) {
486
+ return editor_0?.isActive("bold") ?? false;
487
+ }
488
+ function _temp2(editor_1) {
489
+ return editor_1?.chain().focus().toggleBold().run();
490
+ }
491
+ function _temp3(editor_2) {
492
+ return editor_2?.isActive("italic") ?? false;
493
+ }
494
+ function _temp4(editor_3) {
495
+ return editor_3?.chain().focus().toggleItalic().run();
496
+ }
497
+ function _temp5(editor_4) {
498
+ return editor_4?.isActive("underline") ?? false;
499
+ }
500
+ function _temp6(editor_5) {
501
+ return editor_5?.chain().focus().toggleUnderline().run();
502
+ }
503
+ function _temp7(editor_6) {
504
+ return editor_6?.isActive("strike") ?? false;
505
+ }
506
+ function _temp8(editor_7) {
507
+ return editor_7?.chain().focus().toggleStrike().run();
508
+ }
509
+ function _temp9(editor_8) {
510
+ return editor_8?.isActive("code") ?? false;
511
+ }
512
+ function _temp0(editor_9) {
513
+ return editor_9?.chain().focus().toggleCode().run();
488
514
  }
489
515
  function removeClassesFromJson(jsonObj) {
490
516
  if (Array.isArray(jsonObj)) {
@@ -499,12 +525,158 @@
499
525
  }
500
526
  return jsonObj;
501
527
  }
528
+ const loadingDecorationKey = new state.PluginKey("loadingDecoration");
529
+ const TextLoadingDecorationExtension = core.Extension.create({
530
+ name: "loadingDecoration",
531
+ addOptions() {
532
+ return {
533
+ pluginKey: loadingDecorationKey
534
+ };
535
+ },
536
+ addProseMirrorPlugins() {
537
+ const pluginKey = this.options.pluginKey;
538
+ return [new state.Plugin({
539
+ key: pluginKey,
540
+ state: {
541
+ init() {
542
+ return {
543
+ decorationSet: view.DecorationSet.empty,
544
+ hasDecoration: false
545
+ };
546
+ },
547
+ apply(tr, oldState) {
548
+ const action = tr.getMeta(pluginKey);
549
+ if (action?.type === "loadingDecoration") {
550
+ const {
551
+ pos,
552
+ remove,
553
+ loadingHtml
554
+ } = action;
555
+ if (remove) {
556
+ return {
557
+ decorationSet: view.DecorationSet.empty,
558
+ hasDecoration: false
559
+ };
560
+ }
561
+ const decoration = view.Decoration.widget(pos, () => {
562
+ const container = document.createElement("span");
563
+ container.className = "loading-decoration";
564
+ if (loadingHtml) {
565
+ container.innerHTML = loadingHtml;
566
+ } else {
567
+ const span = document.createElement("span");
568
+ span.innerText = "loading...";
569
+ container.appendChild(span);
570
+ }
571
+ return container;
572
+ });
573
+ return {
574
+ decorationSet: view.DecorationSet.empty.add(tr.doc, [decoration]),
575
+ hasDecoration: true
576
+ };
577
+ }
578
+ return {
579
+ decorationSet: oldState.decorationSet.map(tr.mapping, tr.doc),
580
+ hasDecoration: oldState.hasDecoration
581
+ };
582
+ }
583
+ },
584
+ props: {
585
+ decorations(state2) {
586
+ return this.getState(state2)?.decorationSet || view.DecorationSet.empty;
587
+ }
588
+ }
589
+ })];
590
+ },
591
+ addCommands() {
592
+ return {
593
+ toggleLoadingDecoration: (loadingHtml) => ({
594
+ state: state2,
595
+ dispatch
596
+ }) => {
597
+ const {
598
+ selection
599
+ } = state2;
600
+ const pos = selection.from;
601
+ if (!dispatch) return false;
602
+ const pluginKey = this.options.pluginKey;
603
+ const tr = state2.tr.setMeta(pluginKey, {
604
+ pos,
605
+ type: "loadingDecoration",
606
+ remove: false,
607
+ loadingHtml
608
+ });
609
+ dispatch(tr);
610
+ return true;
611
+ },
612
+ removeLoadingDecoration: () => ({
613
+ state: state2,
614
+ dispatch
615
+ }) => {
616
+ if (!dispatch) return false;
617
+ const pluginKey = this.options.pluginKey;
618
+ const tr = state2.tr.setMeta(pluginKey, {
619
+ pos: 0,
620
+ // We can pass any position as it will remove the entire decoration set
621
+ type: "loadingDecoration",
622
+ remove: true
623
+ });
624
+ dispatch(tr);
625
+ return true;
626
+ }
627
+ };
628
+ }
629
+ });
630
+ const PlaceholderExtension = Placeholder.configure({
631
+ placeholder: ({
632
+ node,
633
+ editor
634
+ }) => {
635
+ const {
636
+ from,
637
+ to
638
+ } = editor.state.selection;
639
+ function hasLoadingDecoration(editor2) {
640
+ const pluginState = loadingDecorationKey.get(editor2.state);
641
+ return pluginState?.getState(editor2.state)?.hasDecoration ?? false;
642
+ }
643
+ const hasDecoration = hasLoadingDecoration(editor);
644
+ if (hasDecoration) {
645
+ return "";
646
+ }
647
+ if (node.type.name === "heading") {
648
+ return `Heading ${node.attrs.level}`;
649
+ }
650
+ if (node.type.name === "paragraph") {
651
+ return "Press '/' for commands";
652
+ }
653
+ return "";
654
+ },
655
+ includeChildren: true
656
+ });
657
+ const Horizontal = HorizontalRule.extend({
658
+ addInputRules() {
659
+ return [new core.InputRule({
660
+ find: /^(?:---|—-|___\s|\*\*\*\s)$/,
661
+ handler: ({
662
+ state: state2,
663
+ range
664
+ }) => {
665
+ const attributes = {};
666
+ const {
667
+ tr
668
+ } = state2;
669
+ const start = range.from;
670
+ const end = range.to;
671
+ tr.insert(start - 1, this.type.create(attributes)).delete(tr.mapping.map(start), tr.mapping.map(end));
672
+ }
673
+ })];
674
+ }
675
+ });
502
676
  const placeholder = PlaceholderExtension;
503
677
  const tiptapLink = TiptapLink.configure({
504
678
  HTMLAttributes: {
505
- class: ui.cls(
506
- "text-gray-600 dark:text-slate-300 underline underline-offset-[3px] hover:text-primary transition-colors cursor-pointer"
507
- )
679
+ class: ui.cls("text-surface-700 dark:text-surface-accent-200 underline underline-offset-[3px] hover:text-primary transition-colors cursor-pointer")
508
680
  }
509
681
  });
510
682
  const taskList = extensionTaskList.TaskList.configure({
@@ -523,38 +695,39 @@
523
695
  class: ui.cls("mt-4 mb-6 border-t", ui.defaultBorderMixin)
524
696
  }
525
697
  });
698
+ const bulletList = BulletList.configure({
699
+ HTMLAttributes: {
700
+ class: ui.cls("list-disc list-outside leading-3 -mt-2")
701
+ }
702
+ });
703
+ const orderedList = OrderedList.configure({
704
+ HTMLAttributes: {
705
+ class: ui.cls("list-decimal list-outside leading-3 -mt-2")
706
+ }
707
+ });
708
+ const listItem = ListItem.configure({
709
+ HTMLAttributes: {
710
+ class: ui.cls("leading-normal -mb-2")
711
+ }
712
+ });
713
+ const blockquote = Blockquote.configure({
714
+ HTMLAttributes: {
715
+ class: ui.cls("border-l-4 border-primary")
716
+ }
717
+ });
718
+ const codeBlock = CodeBlock.configure({
719
+ HTMLAttributes: {
720
+ class: ui.cls("rounded bg-blue-50 dark:bg-surface-700 border p-5 font-mono font-medium", ui.defaultBorderMixin)
721
+ }
722
+ });
723
+ const code = Code.configure({
724
+ HTMLAttributes: {
725
+ class: ui.cls("rounded-md bg-surface-accent-50 dark:bg-surface-700 px-1.5 py-1 font-mono font-medium"),
726
+ spellcheck: "false"
727
+ }
728
+ });
526
729
  const starterKit = StarterKit.configure({
527
- bulletList: {
528
- HTMLAttributes: {
529
- class: ui.cls("list-disc list-outside leading-3 -mt-2")
530
- }
531
- },
532
- orderedList: {
533
- HTMLAttributes: {
534
- class: ui.cls("list-decimal list-outside leading-3 -mt-2")
535
- }
536
- },
537
- listItem: {
538
- HTMLAttributes: {
539
- class: ui.cls("leading-normal -mb-2")
540
- }
541
- },
542
- blockquote: {
543
- HTMLAttributes: {
544
- class: ui.cls("border-l-4 border-primary")
545
- }
546
- },
547
- codeBlock: {
548
- HTMLAttributes: {
549
- class: ui.cls("rounded bg-blue-50 dark:bg-gray-700 border p-5 font-mono font-medium", ui.defaultBorderMixin)
550
- }
551
- },
552
- code: {
553
- HTMLAttributes: {
554
- class: ui.cls("rounded-md bg-slate-50 dark:bg-gray-700 px-1.5 py-1 font-mono font-medium"),
555
- spellcheck: "false"
556
- }
557
- },
730
+ document: false,
558
731
  horizontalRule: false,
559
732
  dropcursor: {
560
733
  color: "#DBEAFE",
@@ -562,27 +735,42 @@
562
735
  },
563
736
  gapcursor: false
564
737
  });
565
- async function onFileRead(plugin, view$1, readerEvent, pos, upload, image) {
566
- const { schema } = view$1.state;
738
+ async function onFileRead(view$1, readerEvent, pos, upload, image) {
739
+ const {
740
+ schema
741
+ } = view$1.state;
742
+ const plugin = view$1.state.plugins.find((p) => p.key === ImagePluginKey.key);
743
+ if (!plugin) {
744
+ console.error("Image plugin not found");
745
+ return;
746
+ }
567
747
  let decorationSet = plugin.getState(view$1.state);
568
748
  const placeholder2 = document.createElement("div");
569
749
  const imageElement = document.createElement("img");
570
- imageElement.setAttribute("class", "opacity-40 rounded-lg border border-stone-200");
750
+ imageElement.setAttribute("class", "opacity-40 rounded-lg border " + ui.defaultBorderMixin);
571
751
  imageElement.src = readerEvent.target?.result;
572
752
  placeholder2.appendChild(imageElement);
573
753
  const deco = view.Decoration.widget(pos, placeholder2);
574
754
  decorationSet = decorationSet?.add(view$1.state.doc, [deco]);
575
- view$1.dispatch(view$1.state.tr.setMeta(plugin, { decorationSet }));
755
+ view$1.dispatch(view$1.state.tr.setMeta(plugin, {
756
+ decorationSet
757
+ }));
576
758
  const src = await upload(image);
577
- console.log("uploaded image", src);
578
- const imageNode = schema.nodes.image.create({ src });
759
+ console.debug("Uploaded image", src);
760
+ const imageNode = schema.nodes.image.create({
761
+ src
762
+ });
579
763
  const tr = view$1.state.tr.replaceWith(pos, pos, imageNode);
580
764
  decorationSet = decorationSet?.remove([deco]);
581
- tr.setMeta(plugin, { decorationSet });
765
+ tr.setMeta(plugin, {
766
+ decorationSet
767
+ });
582
768
  view$1.dispatch(tr);
583
769
  }
584
- const dropImagePlugin = (upload) => {
585
- const plugin = new prosemirrorState.Plugin({
770
+ const ImagePluginKey = new state.PluginKey("imagePlugin");
771
+ const createDropImagePlugin = (upload) => {
772
+ const plugin = new state.Plugin({
773
+ key: ImagePluginKey,
586
774
  state: {
587
775
  // Initialize the plugin state with an empty DecorationSet
588
776
  init: () => view.DecorationSet.empty,
@@ -598,20 +786,25 @@
598
786
  props: {
599
787
  handleDOMEvents: {
600
788
  drop: (view2, event) => {
601
- console.log("drop event", event);
602
789
  if (!event.dataTransfer?.files || event.dataTransfer?.files.length === 0) {
603
790
  return false;
604
791
  }
605
792
  event.preventDefault();
606
793
  const files = Array.from(event.dataTransfer.files);
607
794
  const images = files.filter((file) => /image/i.test(file.type));
608
- if (images.length === 0) return false;
795
+ if (images.length === 0) {
796
+ console.log("No images found in dropped files");
797
+ return false;
798
+ }
609
799
  images.forEach((image) => {
610
- const position = view2.posAtCoords({ left: event.clientX, top: event.clientY });
800
+ const position = view2.posAtCoords({
801
+ left: event.clientX,
802
+ top: event.clientY
803
+ });
611
804
  if (!position) return;
612
805
  const reader = new FileReader();
613
806
  reader.onload = async (readerEvent) => {
614
- await onFileRead(plugin, view2, readerEvent, position.pos, upload, image);
807
+ await onFileRead(view2, readerEvent, position.pos, upload, image);
615
808
  };
616
809
  reader.readAsDataURL(image);
617
810
  });
@@ -621,16 +814,14 @@
621
814
  handlePaste(view2, event, slice) {
622
815
  const items2 = Array.from(event.clipboardData?.items || []);
623
816
  const pos = view2.state.selection.from;
624
- console.log("pos", pos);
625
817
  let anyImageFound = false;
626
818
  items2.forEach((item) => {
627
819
  const image = item.getAsFile();
628
- console.log("image", image);
629
820
  if (image) {
630
821
  anyImageFound = true;
631
822
  const reader = new FileReader();
632
823
  reader.onload = async (readerEvent) => {
633
- await onFileRead(plugin, view2, readerEvent, pos, upload, image);
824
+ await onFileRead(view2, readerEvent, pos, upload, image);
634
825
  };
635
826
  reader.readAsDataURL(image);
636
827
  }
@@ -655,10 +846,10 @@
655
846
  });
656
847
  return plugin;
657
848
  };
658
- const createImageExtension = (uploadFn) => {
849
+ const createImageExtension = (dropImagePlugin) => {
659
850
  return TiptapImage.extend({
660
851
  addProseMirrorPlugins() {
661
- return [dropImagePlugin(uploadFn)];
852
+ return [dropImagePlugin];
662
853
  }
663
854
  }).configure({
664
855
  allowBase64: true,
@@ -671,9 +862,16 @@
671
862
  name: "CustomKeymap",
672
863
  addCommands() {
673
864
  return {
674
- selectTextWithinNodeBoundaries: () => ({ editor, commands }) => {
675
- const { state: state2 } = editor;
676
- const { tr } = state2;
865
+ selectTextWithinNodeBoundaries: () => ({
866
+ editor,
867
+ commands
868
+ }) => {
869
+ const {
870
+ state: state2
871
+ } = editor;
872
+ const {
873
+ tr
874
+ } = state2;
677
875
  const startNodePos = tr.selection.$from.start();
678
876
  const endNodePos = tr.selection.$to.end();
679
877
  return commands.setTextSelection({
@@ -685,9 +883,15 @@
685
883
  },
686
884
  addKeyboardShortcuts() {
687
885
  return {
688
- "Mod-a": ({ editor }) => {
689
- const { state: state2 } = editor;
690
- const { tr } = state2;
886
+ "Mod-a": ({
887
+ editor
888
+ }) => {
889
+ const {
890
+ state: state2
891
+ } = editor;
892
+ const {
893
+ tr
894
+ } = state2;
691
895
  const startSelectionPos = tr.selection.from;
692
896
  const endSelectionPos = tr.selection.to;
693
897
  const startNodePos = tr.selection.$from.start();
@@ -702,20 +906,76 @@
702
906
  };
703
907
  }
704
908
  });
705
- function absoluteRect(node) {
706
- const data = node.getBoundingClientRect();
909
+ function serializeForClipboard(view2, slice) {
910
+ view2.someProp("transformCopied", (f) => {
911
+ slice = f(slice, view2);
912
+ });
913
+ const context = [];
914
+ let {
915
+ content,
916
+ openStart,
917
+ openEnd
918
+ } = slice;
919
+ while (openStart > 1 && openEnd > 1 && content.childCount == 1 && content.firstChild.childCount == 1) {
920
+ openStart--;
921
+ openEnd--;
922
+ const node = content.firstChild;
923
+ context.push(node.type.name, node.attrs != node.type.defaultAttrs ? node.attrs : null);
924
+ content = node.content;
925
+ }
926
+ const serializer = view2.someProp("clipboardSerializer") || model.DOMSerializer.fromSchema(view2.state.schema);
927
+ const doc = detachedDoc(), wrap = doc.createElement("div");
928
+ wrap.appendChild(serializer.serializeFragment(content, {
929
+ document: doc
930
+ }));
931
+ let firstChild = wrap.firstChild, needsWrap, wrappers = 0;
932
+ while (firstChild && firstChild.nodeType == 1 && (needsWrap = wrapMap[firstChild.nodeName.toLowerCase()])) {
933
+ for (let i = needsWrap.length - 1; i >= 0; i--) {
934
+ const wrapper = doc.createElement(needsWrap[i]);
935
+ while (wrap.firstChild) wrapper.appendChild(wrap.firstChild);
936
+ wrap.appendChild(wrapper);
937
+ wrappers++;
938
+ }
939
+ firstChild = wrap.firstChild;
940
+ }
941
+ if (firstChild && firstChild.nodeType == 1) firstChild.setAttribute("data-pm-slice", `${openStart} ${openEnd}${wrappers ? ` -${wrappers}` : ""} ${JSON.stringify(context)}`);
942
+ const text = view2.someProp("clipboardTextSerializer", (f) => f(slice, view2)) || slice.content.textBetween(0, slice.content.size, "\n\n");
943
+ return {
944
+ dom: wrap,
945
+ text,
946
+ slice
947
+ };
948
+ }
949
+ const wrapMap = {
950
+ thead: ["table"],
951
+ tbody: ["table"],
952
+ tfoot: ["table"],
953
+ caption: ["table"],
954
+ colgroup: ["table"],
955
+ col: ["table", "colgroup"],
956
+ tr: ["table", "tbody"],
957
+ td: ["table", "tbody", "tr"],
958
+ th: ["table", "tbody", "tr"]
959
+ };
960
+ let _detachedDoc = null;
961
+ function detachedDoc() {
962
+ return _detachedDoc || (_detachedDoc = document.implementation.createHTMLDocument("title"));
963
+ }
964
+ function absoluteRect(element) {
965
+ const data = element.getBoundingClientRect();
966
+ let ancestor = element.parentElement;
967
+ while (ancestor && window.getComputedStyle(ancestor).position === "static") {
968
+ ancestor = ancestor.parentElement;
969
+ }
970
+ const ancestorRect = ancestor?.getBoundingClientRect();
707
971
  return {
708
- top: data.top,
709
- left: data.left,
972
+ top: data.top - (ancestorRect?.top ?? 0),
973
+ left: data.left - (ancestorRect?.left ?? 0),
710
974
  width: data.width
711
975
  };
712
976
  }
713
977
  function nodeDOMAtCoords(coords) {
714
- return document.elementsFromPoint(coords.x, coords.y).find(
715
- (elem) => elem.parentElement?.matches?.(".ProseMirror") || elem.matches(
716
- ["li", "p:not(:first-child)", "pre", "blockquote", "h1, h2, h3, h4, h5, h6"].join(", ")
717
- )
718
- );
978
+ return document.elementsFromPoint(coords.x, coords.y).find((elem) => elem.parentElement?.matches?.(".ProseMirror") || elem.matches(["li", "p:not(:first-child)", "pre", "blockquote", "h1, h2, h3, h4, h5, h6"].join(", ")));
719
979
  }
720
980
  function nodePosAtDOM(node, view2, options) {
721
981
  const boundingRect = node.getBoundingClientRect();
@@ -725,25 +985,31 @@
725
985
  })?.inside;
726
986
  }
727
987
  function DragHandle(options) {
728
- function handleDragStart(event, view$1) {
729
- view$1.focus();
988
+ function handleDragStart(event, view2) {
989
+ view2.focus();
730
990
  if (!event.dataTransfer) return;
731
991
  const node = nodeDOMAtCoords({
732
992
  x: event.clientX + 50 + options.dragHandleWidth,
733
993
  y: event.clientY
734
994
  });
735
995
  if (!(node instanceof Element)) return;
736
- const nodePos = nodePosAtDOM(node, view$1, options);
996
+ const nodePos = nodePosAtDOM(node, view2, options);
737
997
  if (nodePos == null || nodePos < 0) return;
738
- view$1.dispatch(view$1.state.tr.setSelection(state.NodeSelection.create(view$1.state.doc, nodePos)));
739
- const slice = view$1.state.selection.content();
740
- const { dom, text } = view.__serializeForClipboard(view$1, slice);
998
+ view2.dispatch(view2.state.tr.setSelection(state.NodeSelection.create(view2.state.doc, nodePos)));
999
+ const slice = view2.state.selection.content();
1000
+ const {
1001
+ dom: dom2,
1002
+ text
1003
+ } = serializeForClipboard(view2, slice);
741
1004
  event.dataTransfer.clearData();
742
- event.dataTransfer.setData("text/html", dom.innerHTML);
1005
+ event.dataTransfer.setData("text/html", dom2.innerHTML);
743
1006
  event.dataTransfer.setData("text/plain", text);
744
1007
  event.dataTransfer.effectAllowed = "copyMove";
745
1008
  event.dataTransfer.setDragImage(node, 0, 0);
746
- view$1.dragging = { slice, move: event.ctrlKey };
1009
+ view2.dragging = {
1010
+ slice,
1011
+ move: event.ctrlKey
1012
+ };
747
1013
  }
748
1014
  function handleClick(event, view2) {
749
1015
  view2.focus();
@@ -784,8 +1050,6 @@
784
1050
  view2?.dom?.parentElement?.appendChild(dragHandleElement);
785
1051
  return {
786
1052
  destroy: () => {
787
- dragHandleElement?.remove?.();
788
- dragHandleElement = null;
789
1053
  }
790
1054
  };
791
1055
  },
@@ -841,301 +1105,843 @@
841
1105
  const DragAndDrop = core.Extension.create({
842
1106
  name: "dragAndDrop",
843
1107
  addProseMirrorPlugins() {
844
- return [
845
- DragHandle({
846
- dragHandleWidth: 24
847
- })
848
- ];
1108
+ return [DragHandle({
1109
+ dragHandleWidth: 24
1110
+ })];
849
1111
  }
850
1112
  });
851
- const FireCMSEditor = ({
852
- handleImageUpload,
853
- initialContent,
854
- onJsonContentChange,
855
- onHtmlContentChange,
856
- onMarkdownContentChange
857
- }) => {
858
- const defaultEditorProps = {
859
- handleDOMEvents: {
860
- keydown: (_view, event) => {
861
- if (["ArrowUp", "ArrowDown", "Enter"].includes(event.key)) {
862
- const slashCommand2 = document.querySelector("#slash-command");
863
- if (slashCommand2) {
864
- return true;
1113
+ function buildDecorationSet(highlight, doc) {
1114
+ const decorations = [];
1115
+ if (highlight) {
1116
+ decorations.push(view.Decoration.inline(highlight.from, highlight.to, {
1117
+ class: "dark:bg-surface-accent-700 bg-surface-accent-300"
1118
+ }));
1119
+ }
1120
+ const decorationSet = view.DecorationSet.create(doc, decorations);
1121
+ return decorationSet;
1122
+ }
1123
+ const HighlightDecorationExtension = (initialHighlight) => core.Extension.create({
1124
+ name: "highlightDecoration",
1125
+ addOptions() {
1126
+ return {
1127
+ pluginKey: new state.PluginKey("highlightDecoration"),
1128
+ highlight: initialHighlight
1129
+ };
1130
+ },
1131
+ addProseMirrorPlugins() {
1132
+ const pluginKey = this.options.pluginKey;
1133
+ return [new state.Plugin({
1134
+ key: pluginKey,
1135
+ state: {
1136
+ init: (_, {
1137
+ doc
1138
+ }) => {
1139
+ const highlight = this.options.highlight;
1140
+ const decorationSet = highlight && doc ? buildDecorationSet(highlight, doc) : view.DecorationSet.empty;
1141
+ return {
1142
+ decorationSet,
1143
+ highlight
1144
+ };
1145
+ },
1146
+ apply(transaction, oldState) {
1147
+ const action = transaction.getMeta(pluginKey);
1148
+ const highlight = action?.range;
1149
+ if (action?.type === "highlightDecoration") {
1150
+ const doc = transaction.doc;
1151
+ const {
1152
+ remove
1153
+ } = action;
1154
+ if (remove) {
1155
+ return {
1156
+ decorationSet: view.DecorationSet.empty
1157
+ };
1158
+ }
1159
+ const decorationSet = buildDecorationSet(highlight, doc);
1160
+ return {
1161
+ decorationSet,
1162
+ highlight
1163
+ };
1164
+ } else {
1165
+ return oldState;
865
1166
  }
866
1167
  }
867
- return false;
1168
+ },
1169
+ props: {
1170
+ decorations(state2) {
1171
+ const autocompleteState = this.getState(state2);
1172
+ if (autocompleteState?.decorationSet) {
1173
+ return autocompleteState.decorationSet;
1174
+ } else {
1175
+ return view.DecorationSet.empty;
1176
+ }
1177
+ }
1178
+ }
1179
+ })];
1180
+ },
1181
+ addCommands() {
1182
+ return {
1183
+ toggleAutocompleteHighlight: (range) => ({
1184
+ state: state2,
1185
+ dispatch
1186
+ }) => {
1187
+ const {
1188
+ selection
1189
+ } = state2;
1190
+ const pos = selection.from;
1191
+ if (!dispatch) return false;
1192
+ const pluginKey = this.options.pluginKey;
1193
+ const tr = state2.tr.setMeta(pluginKey, {
1194
+ pos,
1195
+ type: "highlightDecoration",
1196
+ remove: false,
1197
+ range
1198
+ });
1199
+ dispatch(tr);
1200
+ return true;
1201
+ },
1202
+ removeAutocompleteHighlight: () => ({
1203
+ state: state2,
1204
+ dispatch
1205
+ }) => {
1206
+ if (!dispatch) return false;
1207
+ const pluginKey = this.options.pluginKey;
1208
+ const tr = state2.tr.setMeta(pluginKey, {
1209
+ pos: 0,
1210
+ // We can pass any position as it will remove the entire decoration set
1211
+ type: "highlightDecoration",
1212
+ remove: true
1213
+ });
1214
+ dispatch(tr);
1215
+ return true;
1216
+ }
1217
+ };
1218
+ }
1219
+ });
1220
+ const CommandPluginKey = new state.PluginKey("slash-command");
1221
+ const SlashCommand = core.Node.create({
1222
+ name: "command",
1223
+ addOptions() {
1224
+ return {
1225
+ HTMLAttributes: {},
1226
+ renderText({
1227
+ options,
1228
+ node
1229
+ }) {
1230
+ return `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`;
1231
+ },
1232
+ deleteTriggerWithBackspace: false,
1233
+ renderHTML({
1234
+ options,
1235
+ node
1236
+ }) {
1237
+ return ["span", core.mergeAttributes(this.HTMLAttributes, options.HTMLAttributes), `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`];
1238
+ },
1239
+ suggestion: {
1240
+ char: "/",
1241
+ pluginKey: CommandPluginKey,
1242
+ command: ({
1243
+ editor,
1244
+ range,
1245
+ props
1246
+ }) => {
1247
+ const nodeAfter = editor.view.state.selection.$to.nodeAfter;
1248
+ const overrideSpace = nodeAfter?.text?.startsWith(" ");
1249
+ if (overrideSpace) {
1250
+ range.to += 1;
1251
+ }
1252
+ editor.chain().focus().insertContentAt(range, [{
1253
+ type: this.name,
1254
+ attrs: props
1255
+ }, {
1256
+ type: "text",
1257
+ text: " "
1258
+ }]).run();
1259
+ window.getSelection()?.collapseToEnd();
1260
+ },
1261
+ allow: ({
1262
+ state: state2,
1263
+ range
1264
+ }) => {
1265
+ const $from = state2.doc.resolve(range.from);
1266
+ const type = state2.schema.nodes[this.name];
1267
+ return !!$from.parent.type.contentMatch.matchType(type);
1268
+ }
868
1269
  }
1270
+ };
1271
+ },
1272
+ group: "inline",
1273
+ inline: true,
1274
+ selectable: false,
1275
+ atom: true,
1276
+ addAttributes() {
1277
+ return {
1278
+ id: {
1279
+ default: null,
1280
+ parseHTML: (element) => element.getAttribute("data-id"),
1281
+ renderHTML: (attributes) => {
1282
+ if (!attributes.id) {
1283
+ return {};
1284
+ }
1285
+ return {
1286
+ "data-id": attributes.id
1287
+ };
1288
+ }
1289
+ },
1290
+ label: {
1291
+ default: null,
1292
+ parseHTML: (element) => element.getAttribute("data-label"),
1293
+ renderHTML: (attributes) => {
1294
+ if (!attributes.label) {
1295
+ return {};
1296
+ }
1297
+ return {
1298
+ "data-label": attributes.label
1299
+ };
1300
+ }
1301
+ }
1302
+ };
1303
+ },
1304
+ parseHTML() {
1305
+ return [{
1306
+ tag: `span[data-type="${this.name}"]`
1307
+ }];
1308
+ },
1309
+ renderHTML({
1310
+ node,
1311
+ HTMLAttributes
1312
+ }) {
1313
+ if (this.options.renderLabel !== void 0) {
1314
+ console.warn("renderLabel is deprecated use renderText and renderHTML instead");
1315
+ return ["span", core.mergeAttributes({
1316
+ "data-type": this.name
1317
+ }, this.options.HTMLAttributes, HTMLAttributes), this.options.renderLabel({
1318
+ options: this.options,
1319
+ node
1320
+ })];
869
1321
  }
870
- // handlePaste: (view, event) => {
871
- // if (event.clipboardData && event.clipboardData.files && event.clipboardData.files[0]) {
872
- // event.preventDefault();
873
- // const file = event.clipboardData.files[0];
874
- // const pos = view.state.selection.from;
875
- //
876
- // // startImageUpload({ file, view, pos, handleImageUpload });
877
- // return true;
878
- // }
879
- // return false;
880
- // },
881
- // handleDrop: (view, event, _slice, moved) => {
882
- // console.log("handleDrop", { event, moved });
883
- // if (!moved && event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files[0]) {
884
- // console.log("handleDrop!!!", { event, moved });
885
- // event.preventDefault();
886
- // const file = event.dataTransfer.files[0];
887
- // const coordinates = view.posAtCoords({
888
- // left: event.clientX,
889
- // top: event.clientY
890
- // });
891
- // // here we deduct 1 from the pos or else the image will create an extra node
892
- // startImageUpload({
893
- // file,
894
- // view,
895
- // pos: coordinates?.pos || 0 - 1,
896
- // handleImageUpload,
897
- // });
898
- // return true;
899
- // }
900
- // return false;
901
- // }
902
- };
903
- const suggestionItems = createSuggestionItems([
904
- {
905
- title: "Text",
906
- description: "Just start typing with plain text.",
907
- searchTerms: ["p", "paragraph"],
908
- icon: /* @__PURE__ */ jsxRuntime.jsx(ui.TextFieldsIcon, { size: 18 }),
909
- command: ({ editor, range }) => {
910
- editor.chain().focus().deleteRange(range).toggleNode("paragraph", "paragraph").run();
1322
+ const mergedOptions = {
1323
+ ...this.options
1324
+ };
1325
+ mergedOptions.HTMLAttributes = core.mergeAttributes({
1326
+ "data-type": this.name
1327
+ }, this.options.HTMLAttributes, HTMLAttributes);
1328
+ const html = this.options.renderHTML({
1329
+ options: mergedOptions,
1330
+ node
1331
+ });
1332
+ if (typeof html === "string") {
1333
+ return ["span", core.mergeAttributes({
1334
+ "data-type": this.name
1335
+ }, this.options.HTMLAttributes, HTMLAttributes), html];
1336
+ }
1337
+ return html;
1338
+ },
1339
+ renderText({
1340
+ node
1341
+ }) {
1342
+ return this.options.renderText({
1343
+ options: this.options,
1344
+ node
1345
+ });
1346
+ },
1347
+ addKeyboardShortcuts() {
1348
+ return {
1349
+ Backspace: () => this.editor.commands.command(({
1350
+ tr,
1351
+ state: state2
1352
+ }) => {
1353
+ let isCommand = false;
1354
+ const {
1355
+ selection
1356
+ } = state2;
1357
+ const {
1358
+ empty,
1359
+ anchor
1360
+ } = selection;
1361
+ if (!empty) {
1362
+ return false;
1363
+ }
1364
+ state2.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {
1365
+ if (node.type.name === this.name) {
1366
+ isCommand = true;
1367
+ tr.insertText(this.options.deleteTriggerWithBackspace ? "" : this.options.suggestion.char || "", pos, pos + node.nodeSize);
1368
+ return false;
1369
+ }
1370
+ return true;
1371
+ });
1372
+ return isCommand;
1373
+ })
1374
+ };
1375
+ },
1376
+ addProseMirrorPlugins() {
1377
+ return [Suggestion({
1378
+ editor: this.editor,
1379
+ ...this.options.suggestion
1380
+ })];
1381
+ }
1382
+ });
1383
+ const suggestion = (ref, {
1384
+ upload,
1385
+ aiController
1386
+ }) => ({
1387
+ items: ({
1388
+ query
1389
+ }) => {
1390
+ const availableSuggestionItems = [...suggestionItems];
1391
+ if (aiController) {
1392
+ availableSuggestionItems.push(autocompleteSuggestionItem);
1393
+ }
1394
+ return availableSuggestionItems.filter((item) => {
1395
+ const inTitle = item.title.toLowerCase().startsWith(query.toLowerCase());
1396
+ if (inTitle) return inTitle;
1397
+ return item.searchTerms?.some((term) => term.toLowerCase().startsWith(query.toLowerCase()));
1398
+ });
1399
+ },
1400
+ render: () => {
1401
+ let component;
1402
+ let containerEl = null;
1403
+ let cleanupAutoUpdate = null;
1404
+ let reference = null;
1405
+ return {
1406
+ onStart: (props) => {
1407
+ component = new react.ReactRenderer(CommandList, {
1408
+ props: {
1409
+ ...props,
1410
+ upload,
1411
+ aiController
1412
+ },
1413
+ editor: props.editor
1414
+ });
1415
+ if (!props.clientRect) {
1416
+ return;
1417
+ }
1418
+ containerEl = document.createElement("div");
1419
+ containerEl.style.position = "fixed";
1420
+ containerEl.style.left = "0px";
1421
+ containerEl.style.top = "0px";
1422
+ containerEl.style.zIndex = "9999";
1423
+ (ref?.current || document.body).appendChild(containerEl);
1424
+ containerEl.appendChild(component.element);
1425
+ reference = {
1426
+ getBoundingClientRect: props.clientRect
1427
+ };
1428
+ cleanupAutoUpdate = dom.autoUpdate(reference, containerEl, () => {
1429
+ if (!reference) return;
1430
+ dom.computePosition(reference, containerEl, {
1431
+ placement: "bottom-start",
1432
+ middleware: [dom.offset(4), dom.flip(), dom.shift()],
1433
+ strategy: "fixed"
1434
+ }).then(({
1435
+ x,
1436
+ y
1437
+ }) => {
1438
+ Object.assign(containerEl.style, {
1439
+ left: `${x}px`,
1440
+ top: `${y}px`,
1441
+ visibility: "visible"
1442
+ });
1443
+ });
1444
+ });
1445
+ },
1446
+ onUpdate(props) {
1447
+ component.updateProps(props);
1448
+ if (!props.clientRect || !containerEl || !reference) {
1449
+ return;
1450
+ }
1451
+ reference.getBoundingClientRect = props.clientRect;
1452
+ dom.computePosition(reference, containerEl, {
1453
+ placement: "bottom-start",
1454
+ middleware: [dom.offset(4), dom.flip(), dom.shift()],
1455
+ strategy: "fixed"
1456
+ }).then(({
1457
+ x,
1458
+ y
1459
+ }) => {
1460
+ Object.assign(containerEl.style, {
1461
+ left: `${x}px`,
1462
+ top: `${y}px`,
1463
+ visibility: "visible"
1464
+ });
1465
+ });
1466
+ },
1467
+ onKeyDown(props) {
1468
+ if (props.event.key === "Escape") {
1469
+ props.event.preventDefault();
1470
+ return true;
1471
+ }
1472
+ return component.ref?.onKeyDown(props);
1473
+ },
1474
+ onExit() {
1475
+ if (cleanupAutoUpdate) cleanupAutoUpdate();
1476
+ if (containerEl && containerEl.parentNode) {
1477
+ containerEl.parentNode.removeChild(containerEl);
1478
+ }
1479
+ containerEl = null;
1480
+ reference = null;
1481
+ component?.destroy();
911
1482
  }
912
- },
913
- {
914
- title: "To-do List",
915
- description: "Track tasks with a to-do list.",
916
- searchTerms: ["todo", "task", "list", "check", "checkbox"],
917
- icon: /* @__PURE__ */ jsxRuntime.jsx(ui.CheckBoxIcon, { size: 18 }),
918
- command: ({ editor, range }) => {
919
- editor.chain().focus().deleteRange(range).toggleTaskList().run();
1483
+ };
1484
+ }
1485
+ });
1486
+ const CommandList = React.forwardRef((props, ref) => {
1487
+ const $ = reactCompilerRuntime.c(33);
1488
+ const [selectedIndex, setSelectedIndex] = React.useState(0);
1489
+ const {
1490
+ editor
1491
+ } = react.useCurrentEditor();
1492
+ let t0;
1493
+ if ($[0] !== editor || $[1] !== props.aiController || $[2] !== props.range || $[3] !== props.upload) {
1494
+ t0 = (item) => {
1495
+ if (!editor) {
1496
+ return;
920
1497
  }
921
- },
922
- {
923
- title: "Heading 1",
924
- description: "Big section heading.",
925
- searchTerms: ["title", "big", "large"],
926
- icon: /* @__PURE__ */ jsxRuntime.jsx(ui.LooksOneIcon, { size: 18 }),
927
- command: ({ editor, range }) => {
928
- editor.chain().focus().deleteRange(range).setNode("heading", { level: 1 }).run();
1498
+ item?.command?.({
1499
+ editor,
1500
+ range: props.range,
1501
+ upload: props.upload,
1502
+ aiController: props.aiController
1503
+ });
1504
+ };
1505
+ $[0] = editor;
1506
+ $[1] = props.aiController;
1507
+ $[2] = props.range;
1508
+ $[3] = props.upload;
1509
+ $[4] = t0;
1510
+ } else {
1511
+ t0 = $[4];
1512
+ }
1513
+ const selectItem = t0;
1514
+ let t1;
1515
+ if ($[5] !== props.items.length || $[6] !== selectedIndex) {
1516
+ t1 = () => {
1517
+ setSelectedIndex((selectedIndex + props.items.length - 1) % props.items.length);
1518
+ };
1519
+ $[5] = props.items.length;
1520
+ $[6] = selectedIndex;
1521
+ $[7] = t1;
1522
+ } else {
1523
+ t1 = $[7];
1524
+ }
1525
+ const upHandler = t1;
1526
+ let t2;
1527
+ if ($[8] !== props.items.length || $[9] !== selectedIndex) {
1528
+ t2 = () => {
1529
+ setSelectedIndex((selectedIndex + 1) % props.items.length);
1530
+ };
1531
+ $[8] = props.items.length;
1532
+ $[9] = selectedIndex;
1533
+ $[10] = t2;
1534
+ } else {
1535
+ t2 = $[10];
1536
+ }
1537
+ const downHandler = t2;
1538
+ let t3;
1539
+ if ($[11] !== props.items || $[12] !== selectItem || $[13] !== selectedIndex) {
1540
+ t3 = () => {
1541
+ const item_0 = props.items[selectedIndex];
1542
+ selectItem(item_0);
1543
+ };
1544
+ $[11] = props.items;
1545
+ $[12] = selectItem;
1546
+ $[13] = selectedIndex;
1547
+ $[14] = t3;
1548
+ } else {
1549
+ t3 = $[14];
1550
+ }
1551
+ const enterHandler = t3;
1552
+ let t4;
1553
+ if ($[15] === Symbol.for("react.memo_cache_sentinel")) {
1554
+ t4 = () => setSelectedIndex(0);
1555
+ $[15] = t4;
1556
+ } else {
1557
+ t4 = $[15];
1558
+ }
1559
+ let t5;
1560
+ if ($[16] !== props.items) {
1561
+ t5 = [props.items];
1562
+ $[16] = props.items;
1563
+ $[17] = t5;
1564
+ } else {
1565
+ t5 = $[17];
1566
+ }
1567
+ React.useEffect(t4, t5);
1568
+ let t6;
1569
+ if ($[18] !== downHandler || $[19] !== enterHandler || $[20] !== upHandler) {
1570
+ t6 = () => ({
1571
+ onKeyDown: (t72) => {
1572
+ const {
1573
+ event
1574
+ } = t72;
1575
+ if (event.key === "ArrowUp") {
1576
+ upHandler();
1577
+ return true;
1578
+ }
1579
+ if (event.key === "ArrowDown") {
1580
+ downHandler();
1581
+ return true;
1582
+ }
1583
+ if (event.key === "Enter") {
1584
+ enterHandler();
1585
+ return true;
1586
+ }
1587
+ return false;
929
1588
  }
930
- },
931
- {
932
- title: "Heading 2",
933
- description: "Medium section heading.",
934
- searchTerms: ["subtitle", "medium"],
935
- icon: /* @__PURE__ */ jsxRuntime.jsx(ui.LooksTwoIcon, { size: 18 }),
936
- command: ({ editor, range }) => {
937
- editor.chain().focus().deleteRange(range).setNode("heading", { level: 2 }).run();
1589
+ });
1590
+ $[18] = downHandler;
1591
+ $[19] = enterHandler;
1592
+ $[20] = upHandler;
1593
+ $[21] = t6;
1594
+ } else {
1595
+ t6 = $[21];
1596
+ }
1597
+ React.useImperativeHandle(ref, t6);
1598
+ let t7;
1599
+ if ($[22] === Symbol.for("react.memo_cache_sentinel")) {
1600
+ t7 = [];
1601
+ $[22] = t7;
1602
+ } else {
1603
+ t7 = $[22];
1604
+ }
1605
+ const itemRefs = React.useRef(t7);
1606
+ let t8;
1607
+ let t9;
1608
+ if ($[23] !== selectedIndex) {
1609
+ t8 = () => {
1610
+ if (itemRefs.current[selectedIndex]) {
1611
+ itemRefs.current[selectedIndex].scrollIntoView({
1612
+ block: "nearest"
1613
+ });
938
1614
  }
939
- },
940
- {
941
- title: "Heading 3",
942
- description: "Small section heading.",
943
- searchTerms: ["subtitle", "small"],
944
- icon: /* @__PURE__ */ jsxRuntime.jsx(ui.Looks3Icon, { size: 18 }),
945
- command: ({ editor, range }) => {
946
- editor.chain().focus().deleteRange(range).setNode("heading", { level: 3 }).run();
1615
+ };
1616
+ t9 = [selectedIndex];
1617
+ $[23] = selectedIndex;
1618
+ $[24] = t8;
1619
+ $[25] = t9;
1620
+ } else {
1621
+ t8 = $[24];
1622
+ t9 = $[25];
1623
+ }
1624
+ React.useEffect(t8, t9);
1625
+ let t10;
1626
+ if ($[26] === Symbol.for("react.memo_cache_sentinel")) {
1627
+ t10 = ui.cls("text-surface-900 dark:text-white z-50 max-h-[280px] h-auto w-72 overflow-y-auto rounded-md border bg-white dark:bg-surface-900 px-1 py-2 shadow transition-all", ui.defaultBorderMixin);
1628
+ $[26] = t10;
1629
+ } else {
1630
+ t10 = $[26];
1631
+ }
1632
+ let t11;
1633
+ if ($[27] !== props.items || $[28] !== selectItem || $[29] !== selectedIndex) {
1634
+ t11 = props.items.length ? props.items.map((item_1, index) => /* @__PURE__ */ jsxRuntime.jsxs("button", { value: item_1.title, ref: (el) => {
1635
+ if (!el) {
1636
+ return;
947
1637
  }
948
- },
949
- {
950
- title: "Bullet List",
951
- description: "Create a simple bullet list.",
952
- searchTerms: ["unordered", "point"],
953
- icon: /* @__PURE__ */ jsxRuntime.jsx(ui.FormatListBulletedIcon, { size: 18 }),
954
- command: ({ editor, range }) => {
955
- editor.chain().focus().deleteRange(range).toggleBulletList().run();
1638
+ itemRefs.current[index] = el;
1639
+ }, onClick: () => selectItem(item_1), tabIndex: index === selectedIndex ? 0 : -1, "aria-selected": index === selectedIndex, className: ui.cls("flex w-full items-center space-x-2 rounded-md px-2 py-1 text-left text-sm hover:bg-blue-50 hover:dark:bg-surface-700 aria-selected:bg-blue-50 aria-selected:dark:bg-surface-700", index === selectedIndex ? "bg-blue-100 dark:bg-surface-accent-950" : ""), children: [
1640
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: ui.cls("flex h-10 w-10 items-center justify-center rounded-md border bg-white dark:bg-surface-900", ui.defaultBorderMixin), children: item_1.icon }),
1641
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1642
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: item_1.title }),
1643
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-surface-700 dark:text-surface-accent-300", children: item_1.description })
1644
+ ] })
1645
+ ] }, item_1.title)) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "item", children: "No result" });
1646
+ $[27] = props.items;
1647
+ $[28] = selectItem;
1648
+ $[29] = selectedIndex;
1649
+ $[30] = t11;
1650
+ } else {
1651
+ t11 = $[30];
1652
+ }
1653
+ let t12;
1654
+ if ($[31] !== t11) {
1655
+ t12 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: t10, children: t11 });
1656
+ $[31] = t11;
1657
+ $[32] = t12;
1658
+ } else {
1659
+ t12 = $[32];
1660
+ }
1661
+ return t12;
1662
+ });
1663
+ CommandList.displayName = "CommandList";
1664
+ const autocompleteSuggestionItem = {
1665
+ title: "Autocomplete",
1666
+ description: "Add text based on the context.",
1667
+ searchTerms: ["ai"],
1668
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ui.AutoFixHighIcon, { size: 18 }),
1669
+ command: async ({
1670
+ editor,
1671
+ range,
1672
+ aiController
1673
+ }) => {
1674
+ if (!aiController) throw Error("No AiController");
1675
+ editor.chain().focus().deleteRange(range).toggleNode("paragraph", "paragraph").run();
1676
+ const {
1677
+ state: state2
1678
+ } = editor;
1679
+ const {
1680
+ from,
1681
+ to
1682
+ } = state2.selection;
1683
+ const textBeforeCursor = state2.doc.textBetween(0, from, "\n");
1684
+ const textAfterCursor = state2.doc.textBetween(to, state2.doc.content.size, "\n");
1685
+ let buffer = "";
1686
+ const result = await aiController.autocomplete(textBeforeCursor, textAfterCursor, (delta) => {
1687
+ buffer += delta;
1688
+ if (delta.length !== 0) {
1689
+ editor.chain().focus().toggleLoadingDecoration(buffer).run();
956
1690
  }
957
- },
958
- {
959
- title: "Numbered List",
960
- description: "Create a list with numbering.",
961
- searchTerms: ["ordered"],
962
- icon: /* @__PURE__ */ jsxRuntime.jsx(ui.FormatListNumberedIcon, { size: 18 }),
963
- command: ({ editor, range }) => {
964
- editor.chain().focus().deleteRange(range).toggleOrderedList().run();
1691
+ });
1692
+ editor.chain().focus().insertContent(result, {
1693
+ applyInputRules: false,
1694
+ applyPasteRules: false,
1695
+ parseOptions: {
1696
+ preserveWhitespace: false
965
1697
  }
966
- },
967
- {
968
- title: "Quote",
969
- description: "Capture a quote.",
970
- searchTerms: ["blockquote"],
971
- icon: /* @__PURE__ */ jsxRuntime.jsx(ui.FormatQuoteIcon, { size: 18 }),
972
- command: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleNode("paragraph", "paragraph").toggleBlockquote().run()
973
- },
974
- {
975
- title: "Code",
976
- description: "Capture a code snippet.",
977
- searchTerms: ["codeblock"],
978
- icon: /* @__PURE__ */ jsxRuntime.jsx(ui.CodeIcon, { size: 18 }),
979
- command: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleCodeBlock().run()
980
- },
981
- {
982
- title: "Image",
983
- description: "Upload an image from your computer.",
984
- searchTerms: ["photo", "picture", "media"],
985
- icon: /* @__PURE__ */ jsxRuntime.jsx(ui.ImageIcon, { size: 18 }),
986
- command: ({ editor, range }) => {
987
- editor.chain().focus().deleteRange(range).run();
988
- const input = document.createElement("input");
989
- input.type = "file";
990
- input.accept = "image/*";
991
- input.onchange = async () => {
992
- if (input.files?.length) {
993
- const file = input.files[0];
994
- if (!file) return;
995
- editor.view.state.selection.from;
996
- }
997
- };
998
- input.click();
1698
+ }).run();
1699
+ }
1700
+ };
1701
+ const suggestionItems = [{
1702
+ title: "Text",
1703
+ description: "Just start typing with plain text.",
1704
+ searchTerms: ["p", "paragraph"],
1705
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ui.TextFieldsIcon, { size: 18 }),
1706
+ command: ({
1707
+ editor,
1708
+ range
1709
+ }) => {
1710
+ editor.chain().focus().deleteRange(range).toggleNode("paragraph", "paragraph").run();
1711
+ }
1712
+ }, {
1713
+ title: "To-do List",
1714
+ description: "Track tasks with a to-do list.",
1715
+ searchTerms: ["todo", "task", "list", "check", "checkbox"],
1716
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ui.CheckBoxIcon, { size: 18 }),
1717
+ command: ({
1718
+ editor,
1719
+ range
1720
+ }) => {
1721
+ editor.chain().focus().deleteRange(range).toggleTaskList().run();
1722
+ }
1723
+ }, {
1724
+ title: "Heading 1",
1725
+ description: "Big section heading.",
1726
+ searchTerms: ["title", "big", "large"],
1727
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ui.LooksOneIcon, { size: 18 }),
1728
+ command: ({
1729
+ editor,
1730
+ range
1731
+ }) => {
1732
+ editor.chain().focus().deleteRange(range).setNode("heading", {
1733
+ level: 1
1734
+ }).run();
1735
+ }
1736
+ }, {
1737
+ title: "Heading 2",
1738
+ description: "Medium section heading.",
1739
+ searchTerms: ["subtitle", "medium"],
1740
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ui.LooksTwoIcon, { size: 18 }),
1741
+ command: ({
1742
+ editor,
1743
+ range
1744
+ }) => {
1745
+ editor.chain().focus().deleteRange(range).setNode("heading", {
1746
+ level: 2
1747
+ }).run();
1748
+ }
1749
+ }, {
1750
+ title: "Heading 3",
1751
+ description: "Small section heading.",
1752
+ searchTerms: ["subtitle", "small"],
1753
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ui.Looks3Icon, { size: 18 }),
1754
+ command: ({
1755
+ editor,
1756
+ range
1757
+ }) => {
1758
+ editor.chain().focus().deleteRange(range).setNode("heading", {
1759
+ level: 3
1760
+ }).run();
1761
+ }
1762
+ }, {
1763
+ title: "Bullet List",
1764
+ description: "Create a simple bullet list.",
1765
+ searchTerms: ["unordered", "point"],
1766
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ui.FormatListBulletedIcon, { size: 18 }),
1767
+ command: ({
1768
+ editor,
1769
+ range
1770
+ }) => {
1771
+ editor.chain().focus().deleteRange(range).toggleBulletList().run();
1772
+ }
1773
+ }, {
1774
+ title: "Numbered List",
1775
+ description: "Create a list with numbering.",
1776
+ searchTerms: ["ordered"],
1777
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ui.FormatListNumberedIcon, { size: 18 }),
1778
+ command: ({
1779
+ editor,
1780
+ range
1781
+ }) => {
1782
+ editor.chain().focus().deleteRange(range).toggleOrderedList().run();
1783
+ }
1784
+ }, {
1785
+ title: "Quote",
1786
+ description: "Capture a quote.",
1787
+ searchTerms: ["blockquote"],
1788
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ui.FormatQuoteIcon, { size: 18 }),
1789
+ command: ({
1790
+ editor,
1791
+ range
1792
+ }) => editor.chain().focus().deleteRange(range).toggleNode("paragraph", "paragraph").toggleBlockquote().run()
1793
+ }, {
1794
+ title: "Code",
1795
+ description: "Capture a code snippet.",
1796
+ searchTerms: ["codeblock"],
1797
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ui.CodeIcon, { size: 18 }),
1798
+ command: ({
1799
+ editor,
1800
+ range
1801
+ }) => editor.chain().focus().deleteRange(range).toggleCodeBlock().run()
1802
+ }, {
1803
+ title: "Image",
1804
+ description: "Upload an image from your computer.",
1805
+ searchTerms: ["photo", "picture", "media", "upload", "file"],
1806
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ui.ImageIcon, { size: 18 }),
1807
+ command: ({
1808
+ editor,
1809
+ range,
1810
+ upload
1811
+ }) => {
1812
+ editor.chain().focus().deleteRange(range).run();
1813
+ const input = document.createElement("input");
1814
+ input.type = "file";
1815
+ input.accept = "image/*";
1816
+ input.onchange = async () => {
1817
+ if (input.files?.length) {
1818
+ const file = input.files[0];
1819
+ if (!file) return;
1820
+ const pos = editor.view.state.selection.from;
1821
+ const fileList = input.files;
1822
+ const files = Array.from(fileList);
1823
+ const images = files.filter((file2) => /image/i.test(file2.type));
1824
+ if (images.length === 0) {
1825
+ console.log("No images found in uploaded files");
1826
+ return false;
1827
+ }
1828
+ const view2 = editor.view;
1829
+ images.forEach((image) => {
1830
+ const reader = new FileReader();
1831
+ reader.onload = async (readerEvent) => {
1832
+ await onFileRead(view2, readerEvent, pos, upload, image);
1833
+ };
1834
+ reader.readAsDataURL(image);
1835
+ });
999
1836
  }
1000
- }
1001
- ]);
1002
- const slashCommand = Command.configure({
1003
- suggestion: {
1004
- items: () => suggestionItems,
1005
- render: renderItems
1006
- }
1007
- });
1008
- const imageExtension = React.useMemo(() => createImageExtension(handleImageUpload), []);
1009
- const extensions = [
1010
- TiptapUnderline,
1011
- TextStyle,
1012
- extensionColor.Color,
1013
- Highlight.configure({
1014
- multicolor: true
1015
- }),
1016
- tiptapMarkdown.Markdown.configure({
1017
- html: false,
1018
- transformCopiedText: true
1019
- }),
1020
- CustomKeymap,
1021
- DragAndDrop,
1022
- starterKit,
1023
- placeholder,
1024
- tiptapLink,
1025
- // tiptapImage,
1026
- imageExtension,
1027
- // updatedImage,
1028
- taskList,
1029
- taskItem,
1030
- horizontalRule,
1031
- slashCommand
1032
- ];
1837
+ return true;
1838
+ };
1839
+ input.click();
1840
+ }
1841
+ }];
1842
+ const proseClasses = {
1843
+ "sm": "prose-sm",
1844
+ "base": "prose-base",
1845
+ "lg": "prose-lg"
1846
+ };
1847
+ const canUseDOM = Boolean(typeof window !== "undefined" && window.document && window.document.createElement);
1848
+ const FireCMSEditor = ({
1849
+ content,
1850
+ onJsonContentChange,
1851
+ onHtmlContentChange,
1852
+ onMarkdownContentChange,
1853
+ version,
1854
+ textSize = "base",
1855
+ highlight,
1856
+ handleImageUpload,
1857
+ aiController,
1858
+ disabled
1859
+ }) => {
1860
+ const ref = React.useRef(null);
1861
+ const editorRef = React.useRef(null);
1862
+ const imagePlugin = createDropImagePlugin(handleImageUpload);
1863
+ const imageExtension = React.useMemo(() => createImageExtension(imagePlugin), []);
1033
1864
  const [openNode, setOpenNode] = React.useState(false);
1034
1865
  const [openLink, setOpenLink] = React.useState(false);
1035
1866
  ui.useInjectStyles("Editor", cssStyles);
1036
- const editorRef = React.useRef(null);
1037
- const [markdownContent, setMarkdownContent] = React.useState(null);
1038
- const [jsonContent, setJsonContent] = React.useState(null);
1039
- const [htmlContent, setHtmlContent] = React.useState(null);
1867
+ const deferredHighlight = React.useDeferredValue(highlight);
1868
+ React.useEffect(() => {
1869
+ if (version === void 0) return;
1870
+ if (version > -1 && editorRef.current) {
1871
+ editorRef.current?.commands.setContent(content ?? "");
1872
+ }
1873
+ }, [version]);
1874
+ React.useEffect(() => {
1875
+ editorRef?.current?.setEditable(!disabled);
1876
+ }, [disabled]);
1877
+ React.useEffect(() => {
1878
+ if (version === void 0) return;
1879
+ if (editorRef.current && version > 0) {
1880
+ const chain = editorRef.current.chain();
1881
+ if (deferredHighlight) {
1882
+ chain.focus().toggleAutocompleteHighlight(deferredHighlight).run();
1883
+ } else {
1884
+ chain.focus().removeAutocompleteHighlight().run();
1885
+ }
1886
+ }
1887
+ }, [deferredHighlight?.from, deferredHighlight?.to]);
1888
+ const firstUpdateRef = React.useRef(true);
1040
1889
  const onEditorUpdate = (editor) => {
1041
1890
  editorRef.current = editor;
1891
+ if (firstUpdateRef.current) {
1892
+ firstUpdateRef.current = false;
1893
+ return;
1894
+ }
1042
1895
  if (onMarkdownContentChange) {
1043
- setMarkdownContent(editor.storage.markdown.getMarkdown());
1896
+ const markdown = editorRef.current.storage.markdown.getMarkdown();
1897
+ onMarkdownContentChange?.(addLineBreakAfterImages(markdown));
1044
1898
  }
1045
1899
  if (onJsonContentChange) {
1046
- setJsonContent(removeClassesFromJson(editor.getJSON()));
1900
+ const jsonContent = removeClassesFromJson(editor.getJSON());
1901
+ onJsonContentChange(jsonContent);
1047
1902
  }
1048
1903
  if (onHtmlContentChange) {
1049
- setHtmlContent(editor.getHTML());
1904
+ onHtmlContentChange?.(editor.getHTML());
1050
1905
  }
1051
1906
  };
1052
- useDebouncedCallback(markdownContent, () => {
1053
- if (editorRef.current) {
1054
- const markdown = editorRef.current.storage.markdown.getMarkdown();
1055
- onMarkdownContentChange?.(addLineBreakAfterImages(markdown));
1056
- }
1057
- }, false, 500);
1058
- useDebouncedCallback(jsonContent, () => {
1059
- if (jsonContent)
1060
- onJsonContentChange?.(jsonContent);
1061
- }, false, 500);
1062
- useDebouncedCallback(htmlContent, () => {
1063
- if (htmlContent)
1064
- onHtmlContentChange?.(htmlContent);
1065
- }, false, 500);
1066
- if (!initialContent) return null;
1067
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full p-8", children: /* @__PURE__ */ jsxRuntime.jsx(EditorRoot, { children: /* @__PURE__ */ jsxRuntime.jsx(
1068
- "div",
1069
- {
1070
- className: "relative min-h-[500px] w-full bg-white dark:bg-gray-950 rounded-lg",
1071
- children: /* @__PURE__ */ jsxRuntime.jsxs(
1072
- react.EditorProvider,
1073
- {
1074
- content: initialContent,
1075
- extensions,
1076
- editorProps: {
1077
- ...defaultEditorProps,
1078
- attributes: {
1079
- class: "prose-lg prose-headings:font-title font-default focus:outline-none max-w-full p-12"
1080
- }
1081
- },
1082
- onUpdate: ({ editor }) => {
1083
- console.debug("Editor updated");
1084
- onEditorUpdate(editor);
1085
- },
1086
- children: [
1087
- /* @__PURE__ */ jsxRuntime.jsxs(
1088
- EditorCommand,
1089
- {
1090
- className: ui.cls("text-gray-900 dark:text-white z-50 h-auto max-h-[330px] w-72 overflow-y-auto rounded-md border bg-white dark:bg-gray-900 px-1 py-2 shadow transition-all", ui.defaultBorderMixin),
1091
- children: [
1092
- /* @__PURE__ */ jsxRuntime.jsx(EditorCommandEmpty, { className: "px-2 text-gray-700 dark:text-slate-300", children: "No results" }),
1093
- suggestionItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
1094
- EditorCommandItem,
1095
- {
1096
- value: item.title,
1097
- onCommand: (val) => item?.command?.(val),
1098
- className: "flex w-full items-center space-x-2 rounded-md px-2 py-1 text-left text-sm hover:bg-blue-50 hover:dark:bg-gray-700 aria-selected:bg-blue-50 aria-selected:dark:bg-gray-700",
1099
- children: [
1100
- /* @__PURE__ */ jsxRuntime.jsx(
1101
- "div",
1102
- {
1103
- className: ui.cls("flex h-10 w-10 items-center justify-center rounded-md border bg-white dark:bg-gray-900", ui.defaultBorderMixin),
1104
- children: item.icon
1105
- }
1106
- ),
1107
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1108
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: item.title }),
1109
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-700 dark:text-slate-300", children: item.description })
1110
- ] })
1111
- ]
1112
- },
1113
- item.title
1114
- ))
1115
- ]
1116
- }
1117
- ),
1118
- /* @__PURE__ */ jsxRuntime.jsxs(
1119
- EditorBubble,
1120
- {
1121
- tippyOptions: {
1122
- placement: "top"
1123
- },
1124
- className: ui.cls("flex w-fit max-w-[90vw] h-10 overflow-hidden rounded border bg-white dark:bg-gray-900 shadow", ui.defaultBorderMixin),
1125
- children: [
1126
- /* @__PURE__ */ jsxRuntime.jsx(NodeSelector, { open: openNode, onOpenChange: setOpenNode }),
1127
- /* @__PURE__ */ jsxRuntime.jsx(ui.Separator, { orientation: "vertical" }),
1128
- /* @__PURE__ */ jsxRuntime.jsx(LinkSelector, { open: openLink, onOpenChange: setOpenLink }),
1129
- /* @__PURE__ */ jsxRuntime.jsx(ui.Separator, { orientation: "vertical" }),
1130
- /* @__PURE__ */ jsxRuntime.jsx(TextButtons, {})
1131
- ]
1132
- }
1133
- )
1134
- ]
1135
- }
1136
- )
1907
+ const proseClass = proseClasses[textSize];
1908
+ const extensions = React.useMemo(() => [starterKit, Document.extend({}), HighlightDecorationExtension(highlight), TextLoadingDecorationExtension, Underline, Bold, extensionTextStyle.TextStyleKit, Italic, Strike, Color, Highlight.configure({
1909
+ multicolor: true
1910
+ }), Heading, CustomKeymap, DragAndDrop, placeholder, tiptapLink, imageExtension, taskList, taskItem, tiptapMarkdown.Markdown.configure({
1911
+ html: true
1912
+ }), horizontalRule, bulletList, orderedList, listItem, blockquote, codeBlock, code, SlashCommand.configure({
1913
+ HTMLAttributes: {
1914
+ class: "mention"
1915
+ },
1916
+ suggestion: suggestion(ref, {
1917
+ upload: handleImageUpload,
1918
+ aiController
1919
+ })
1920
+ })], []);
1921
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: "relative min-h-[300px] w-full", children: /* @__PURE__ */ jsxRuntime.jsx(react.EditorProvider, { content: content ?? "", extensions, immediatelyRender: canUseDOM, editorProps: {
1922
+ editable: () => !disabled,
1923
+ attributes: {
1924
+ class: ui.cls(proseClass, "prose-headings:font-title font-default focus:outline-none max-w-full p-12")
1137
1925
  }
1138
- ) }) });
1926
+ }, onCreate: ({
1927
+ editor: editor_0
1928
+ }) => {
1929
+ editorRef.current = editor_0;
1930
+ editor_0.setEditable(!disabled);
1931
+ }, onUpdate: ({
1932
+ editor: editor_1
1933
+ }) => {
1934
+ onEditorUpdate(editor_1);
1935
+ }, children: /* @__PURE__ */ jsxRuntime.jsxs(EditorBubble, { options: {
1936
+ placement: "top",
1937
+ offset: 6
1938
+ }, className: ui.cls("flex w-fit max-w-[90vw] h-10 overflow-hidden rounded border bg-white dark:bg-surface-900 shadow", ui.defaultBorderMixin), children: [
1939
+ /* @__PURE__ */ jsxRuntime.jsx(NodeSelector, { portalContainer: ref.current, open: openNode, onOpenChange: setOpenNode }),
1940
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Separator, { orientation: "vertical" }),
1941
+ /* @__PURE__ */ jsxRuntime.jsx(LinkSelector, { open: openLink, onOpenChange: setOpenLink }),
1942
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Separator, { orientation: "vertical" }),
1943
+ /* @__PURE__ */ jsxRuntime.jsx(TextButtons, {})
1944
+ ] }) }) });
1139
1945
  };
1140
1946
  function addLineBreakAfterImages(markdown) {
1141
1947
  const imageRegex = /!\[.*?\]\(.*?\)/g;
@@ -1143,7 +1949,9 @@
1143
1949
  `);
1144
1950
  }
1145
1951
  const cssStyles = `
1146
-
1952
+ .ProseMirror {
1953
+ box-shadow: none !important;
1954
+ }
1147
1955
  .ProseMirror .is-editor-empty:first-child::before {
1148
1956
  content: attr(data-placeholder);
1149
1957
  float: left;
@@ -1166,6 +1974,7 @@
1166
1974
  }
1167
1975
 
1168
1976
  .is-empty {
1977
+ cursor: text;
1169
1978
  color: rgb(100 116 139); //500
1170
1979
  }
1171
1980
 
@@ -1183,6 +1992,7 @@
1183
1992
  &.ProseMirror-selectednode {
1184
1993
  outline: 3px solid #5abbf7;
1185
1994
  filter: brightness(90%);
1995
+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000) !important;
1186
1996
  }
1187
1997
  }
1188
1998
 
@@ -1210,7 +2020,7 @@ ul[data-type="taskList"] li > label {
1210
2020
  }
1211
2021
 
1212
2022
  &:active {
1213
- background-color: rgb(71 85 105);;
2023
+ background-color: rgb(71 85 105);
1214
2024
  }
1215
2025
  }
1216
2026
  }
@@ -1277,7 +2087,7 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p {
1277
2087
  }
1278
2088
 
1279
2089
  .ProseMirror:not(.dragging) .ProseMirror-selectednode {
1280
- outline: none !important;
2090
+ // outline: none !important;
1281
2091
  background-color: rgb(219 234 254); // blue 100
1282
2092
  transition: background-color 0.2s;
1283
2093
  box-shadow: none;
@@ -1287,19 +2097,23 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p {
1287
2097
  background-color: rgb(51 65 85); // 700
1288
2098
  }
1289
2099
 
2100
+ .prose-base table p {
2101
+ margin: 0;
2102
+ }
2103
+
1290
2104
  .drag-handle {
1291
- position: fixed;
2105
+ position: absolute;
1292
2106
  opacity: 1;
1293
2107
  transition: opacity ease-in 0.2s;
1294
2108
  border-radius: 0.25rem;
1295
2109
 
1296
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10' style='fill: rgba(128, 128, 128, 0.9)'%3E%3Cpath d='M3,2 C2.44771525,2 2,1.55228475 2,1 C2,0.44771525 2.44771525,0 3,0 C3.55228475,0 4,0.44771525 4,1 C4,1.55228475 3.55228475,2 3,2 Z M3,6 C2.44771525,6 2,5.55228475 2,5 C2,4.44771525 2.44771525,4 3,4 C3.55228475,4 4,4.44771525 4,5 C4,5.55228475 3.55228475,6 3,6 Z M3,10 C2.44771525,10 2,9.55228475 2,9 C2,8.44771525 2.44771525,8 3,8 C3.55228475,8 4,8.44771525 4,9 C4,9.55228475 3.55228475,10 3,10 Z M7,2 C6.44771525,2 6,1.55228475 6,1 C6,0.44771525 6.44771525,0 7,0 C7.55228475,0 8,0.44771525 8,1 C8,1.55228475 7.55228475,2 7,2 Z M7,6 C6.44771525,6 6,5.55228475 6,5 C6,4.44771525 6.44771525,4 7,4 C7.55228475,4 8,4.44771525 8,5 C8,5.55228475 7.55228475,6 7,6 Z M7,10 C6.44771525,10 6,9.55228475 6,9 C6,8.44771525 6.44771525,8 7,8 C7.55228475,8 8,8.44771525 8,9 C8,9.55228475 7.55228475,10 7,10 Z'%3E%3C/path%3E%3C/svg%3E");
2110
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10' style='fill: rgba(128, 128, 128, 0.9)'%3E%3Cpath d='M3,2 C2.44771525,2 2,1.55228475 2,1 C2,0.44771525 2.44771525,0 3,0 C3.55228475,0 4,0.44771525 4,1 C4,1.55228475 3.55228475,2 3,2 Z M3,6 C2.44771525,6 2,5.55228475 2,5 C2,4.44771525 2.44771525,4 3,4 C3.55228475,4 4,4.44771525 4,5 C4,5.55228475 3.55228475,6 3,6 Z M3,10 C2.44771525,10 2,9.55228475 2,9 C2,8.44771525 2.44771525,8 3,8 C3.55228475,8 4,8.44771525 4,9 C4,9.55228475 3.55228475,10 3,10 Z M7,2 C6.44771525,2 6,1.55228475 6,1 C6,0.44771525 6.44771525,0 7,0 C7.55228475,0 8,0.44771525 8,1 C8,1.55228475 7.55228475,2 7,2 Z M7,6 C6.44771525,6 6,5.55228475 6,5 C6,4.44771525 6.44771525,4 7,4 C7.55228475,4 8,4.44771525 8,5 C8,5.55228475 7.55228475,6 7,6 Z M7,10 C6.44771525,10 6,9.55228475 6,9 C6,8.44771525 6.44771525,8 7,8 C7,8.44771525 7,9 7,9 C7,9.55228475 7,10 7,10 Z'%3E%3C/path%3E%3C/svg%3E");
1297
2111
  background-size: calc(0.5em + 0.375rem) calc(0.5em + 0.375rem);
1298
2112
  background-repeat: no-repeat;
1299
2113
  background-position: center;
1300
2114
  width: 1.2rem;
1301
2115
  height: 1.5rem;
1302
- z-index: 50;
2116
+ z-index: 100;
1303
2117
  cursor: grab;
1304
2118
 
1305
2119
  &:hover {
@@ -1335,5 +2149,5 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p {
1335
2149
  `;
1336
2150
  exports2.FireCMSEditor = FireCMSEditor;
1337
2151
  Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
1338
- });
2152
+ }));
1339
2153
  //# sourceMappingURL=index.umd.js.map