neuphlo-editor 1.8.2 → 2.0.0

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.
Files changed (41) hide show
  1. package/dist/chunk-2DWEJI45.js +1296 -0
  2. package/dist/chunk-2DWEJI45.js.map +1 -0
  3. package/dist/chunk-457ETWB6.js +1351 -0
  4. package/dist/chunk-457ETWB6.js.map +1 -0
  5. package/dist/chunk-62DYB7FY.js +1305 -0
  6. package/dist/chunk-62DYB7FY.js.map +1 -0
  7. package/dist/chunk-DWGPGRTQ.js +1302 -0
  8. package/dist/chunk-DWGPGRTQ.js.map +1 -0
  9. package/dist/chunk-EG7NQJRA.js +1324 -0
  10. package/dist/chunk-EG7NQJRA.js.map +1 -0
  11. package/dist/chunk-FLLPFFI5.js +1296 -0
  12. package/dist/chunk-FLLPFFI5.js.map +1 -0
  13. package/dist/chunk-FVQHB6VC.js +1128 -0
  14. package/dist/chunk-FVQHB6VC.js.map +1 -0
  15. package/dist/chunk-GXJGZHKR.js +1326 -0
  16. package/dist/chunk-GXJGZHKR.js.map +1 -0
  17. package/dist/chunk-KCPPTLGY.js +1299 -0
  18. package/dist/chunk-KCPPTLGY.js.map +1 -0
  19. package/dist/chunk-LHG2NX6C.js +1123 -0
  20. package/dist/chunk-LHG2NX6C.js.map +1 -0
  21. package/dist/chunk-OCNM37WJ.js +1289 -0
  22. package/dist/chunk-OCNM37WJ.js.map +1 -0
  23. package/dist/chunk-RW6QBMJB.js +1300 -0
  24. package/dist/chunk-RW6QBMJB.js.map +1 -0
  25. package/dist/chunk-SOXTEP7H.js +6705 -0
  26. package/dist/chunk-SOXTEP7H.js.map +1 -0
  27. package/dist/headless/index.cjs +207 -144
  28. package/dist/headless/index.cjs.map +1 -1
  29. package/dist/headless/index.d.cts +9 -25
  30. package/dist/headless/index.d.ts +9 -25
  31. package/dist/headless/index.js +1 -1
  32. package/dist/react/index.cjs +1839 -723
  33. package/dist/react/index.cjs.map +1 -1
  34. package/dist/react/index.css +364 -8
  35. package/dist/react/index.css.map +1 -1
  36. package/dist/react/index.d.cts +10 -2
  37. package/dist/react/index.d.ts +10 -2
  38. package/dist/react/index.js +1434 -547
  39. package/dist/react/index.js.map +1 -1
  40. package/dist/styles.css +410 -8
  41. package/package.json +7 -2
@@ -42,17 +42,17 @@ __export(headless_exports, {
42
42
  EditorRoot: () => EditorRoot,
43
43
  MentionCommand: () => MentionCommand,
44
44
  Placeholder: () => import_extension_placeholder.Placeholder,
45
- SlashCommand: () => Command2,
45
+ SlashCommand: () => Command,
46
46
  StarterKit: () => import_starter_kit.StarterKit,
47
47
  createMentionExtension: () => createMentionExtension,
48
48
  createSuggestionItems: () => createSuggestionItems,
49
49
  handleCommandNavigation: () => handleCommandNavigation,
50
50
  renderMentionSuggestion: () => renderMentionSuggestion,
51
51
  renderSlashCommandItems: () => renderItems,
52
- useEditor: () => import_react13.useCurrentEditor
52
+ useEditor: () => import_react12.useCurrentEditor
53
53
  });
54
54
  module.exports = __toCommonJS(headless_exports);
55
- var import_react13 = require("@tiptap/react");
55
+ var import_react12 = require("@tiptap/react");
56
56
 
57
57
  // src/headless/components/editor.tsx
58
58
  var import_react = require("@tiptap/react");
@@ -122,109 +122,178 @@ EditorBubbleItem.displayName = "EditorBubbleItem";
122
122
  // src/headless/components/editor-command.tsx
123
123
  var import_jotai4 = require("jotai");
124
124
  var import_react6 = require("react");
125
- var import_cmdk = require("cmdk");
125
+ var import_react_dom = require("react-dom");
126
126
 
127
127
  // src/headless/utils/atoms.ts
128
128
  var import_jotai3 = require("jotai");
129
129
  var queryAtom = (0, import_jotai3.atom)("");
130
130
  var rangeAtom = (0, import_jotai3.atom)(null);
131
+ var slashMenuOpenAtom = (0, import_jotai3.atom)(false);
132
+ var slashMenuRectAtom = (0, import_jotai3.atom)(null);
131
133
 
132
134
  // src/headless/components/editor-command.tsx
133
- var import_tunnel_rat = __toESM(require("tunnel-rat"), 1);
134
135
  var import_jsx_runtime4 = require("react/jsx-runtime");
135
- var commandTunnel = (0, import_tunnel_rat.default)();
136
- var EditorCommandOut = ({
137
- query,
138
- range
139
- }) => {
140
- const setQuery = (0, import_jotai4.useSetAtom)(queryAtom, { store: novelStore });
141
- const setRange = (0, import_jotai4.useSetAtom)(rangeAtom, { store: novelStore });
142
- (0, import_react6.useEffect)(() => {
143
- setQuery(query);
144
- }, [query, setQuery]);
145
- (0, import_react6.useEffect)(() => {
146
- setRange(range);
147
- }, [range, setRange]);
148
- (0, import_react6.useEffect)(() => {
149
- const navigationKeys = ["ArrowUp", "ArrowDown", "Enter"];
150
- const onKeyDown = (e) => {
151
- if (navigationKeys.includes(e.key)) {
152
- e.preventDefault();
153
- const commandRef = document.querySelector("#slash-command");
154
- if (commandRef)
155
- commandRef.dispatchEvent(
156
- new KeyboardEvent("keydown", {
157
- key: e.key,
158
- cancelable: true,
159
- bubbles: true
160
- })
161
- );
162
- return false;
163
- }
164
- };
165
- document.addEventListener("keydown", onKeyDown);
166
- return () => {
167
- document.removeEventListener("keydown", onKeyDown);
168
- };
169
- }, []);
170
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(commandTunnel.Out, {});
171
- };
172
- var CommandAny = import_cmdk.Command;
136
+ var EditorCommandOut = () => null;
173
137
  var EditorCommand = (0, import_react6.forwardRef)(
174
138
  ({ children, className, ...rest }, ref) => {
175
- const [query, setQuery] = (0, import_jotai4.useAtom)(queryAtom);
176
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(commandTunnel.In, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
177
- CommandAny,
178
- {
179
- ref,
180
- onKeyDown: (e) => {
181
- e.stopPropagation();
182
- },
183
- id: "slash-command",
184
- className,
185
- ...rest,
186
- children: [
187
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
188
- CommandAny.Input,
189
- {
190
- value: query,
191
- onValueChange: setQuery,
192
- style: { display: "none" }
193
- }
194
- ),
195
- children
196
- ]
139
+ const isOpen = (0, import_jotai4.useAtomValue)(slashMenuOpenAtom, { store: novelStore });
140
+ const rect = (0, import_jotai4.useAtomValue)(slashMenuRectAtom, { store: novelStore });
141
+ const containerRef = (0, import_react6.useRef)(null);
142
+ const [activeIndex, setActiveIndex] = (0, import_react6.useState)(0);
143
+ const contentRef = (0, import_react6.useRef)(null);
144
+ const query = (0, import_jotai4.useAtomValue)(queryAtom, { store: novelStore });
145
+ (0, import_react6.useEffect)(() => {
146
+ setActiveIndex(0);
147
+ }, [query]);
148
+ if (typeof document !== "undefined" && !containerRef.current) {
149
+ const el = document.createElement("div");
150
+ el.style.position = "fixed";
151
+ el.style.zIndex = "9999";
152
+ el.style.minWidth = "240px";
153
+ el.style.display = "none";
154
+ containerRef.current = el;
155
+ }
156
+ (0, import_react6.useEffect)(() => {
157
+ const el = containerRef.current;
158
+ if (!el) return;
159
+ document.body.appendChild(el);
160
+ return () => {
161
+ el.remove();
162
+ };
163
+ }, []);
164
+ (0, import_react6.useLayoutEffect)(() => {
165
+ const container = containerRef.current;
166
+ if (!container) return;
167
+ if (!isOpen || !rect) {
168
+ container.style.display = "none";
169
+ return;
170
+ }
171
+ container.style.display = "";
172
+ const menuHeight = container.offsetHeight || 360;
173
+ const viewportHeight = window.innerHeight;
174
+ const spaceBelow = viewportHeight - rect.bottom;
175
+ const spaceAbove = rect.top;
176
+ let top;
177
+ if (spaceBelow < menuHeight + 16 && spaceAbove > spaceBelow) {
178
+ top = Math.round(rect.top - menuHeight - 8);
179
+ if (top < 8) top = 8;
180
+ } else {
181
+ top = Math.round(rect.bottom + 8);
197
182
  }
198
- ) });
183
+ let left = Math.round(rect.left);
184
+ const menuWidth = container.offsetWidth || 280;
185
+ if (left + menuWidth > window.innerWidth - 8) {
186
+ left = window.innerWidth - menuWidth - 8;
187
+ }
188
+ if (left < 8) left = 8;
189
+ container.style.top = `${top}px`;
190
+ container.style.left = `${left}px`;
191
+ }, [isOpen, rect]);
192
+ const handleKeyDown = (0, import_react6.useCallback)(
193
+ (e) => {
194
+ if (!isOpen || !contentRef.current) return;
195
+ const items = contentRef.current.querySelectorAll("[role='option']");
196
+ if (items.length === 0) return;
197
+ if (e.key === "ArrowDown") {
198
+ e.preventDefault();
199
+ setActiveIndex((prev) => {
200
+ const next = Math.min(prev + 1, items.length - 1);
201
+ items[next]?.scrollIntoView({ block: "nearest" });
202
+ return next;
203
+ });
204
+ } else if (e.key === "ArrowUp") {
205
+ e.preventDefault();
206
+ setActiveIndex((prev) => {
207
+ const next = Math.max(prev - 1, 0);
208
+ items[next]?.scrollIntoView({ block: "nearest" });
209
+ return next;
210
+ });
211
+ } else if (e.key === "Enter") {
212
+ e.preventDefault();
213
+ const activeItem = items[activeIndex];
214
+ activeItem?.click();
215
+ }
216
+ },
217
+ [isOpen, activeIndex]
218
+ );
219
+ (0, import_react6.useEffect)(() => {
220
+ if (!isOpen) return;
221
+ document.addEventListener("keydown", handleKeyDown);
222
+ return () => document.removeEventListener("keydown", handleKeyDown);
223
+ }, [isOpen, handleKeyDown]);
224
+ (0, import_react6.useEffect)(() => {
225
+ if (!contentRef.current) return;
226
+ const items = contentRef.current.querySelectorAll("[role='option']");
227
+ items.forEach((item, i) => {
228
+ if (i === activeIndex) {
229
+ item.setAttribute("aria-selected", "true");
230
+ } else {
231
+ item.removeAttribute("aria-selected");
232
+ }
233
+ });
234
+ });
235
+ if (!isOpen || !containerRef.current) return null;
236
+ return (0, import_react_dom.createPortal)(
237
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
238
+ "div",
239
+ {
240
+ ref: (el) => {
241
+ contentRef.current = el;
242
+ if (typeof ref === "function") ref(el);
243
+ else if (ref) ref.current = el;
244
+ },
245
+ id: "slash-command",
246
+ className,
247
+ ...rest,
248
+ children
249
+ }
250
+ ),
251
+ containerRef.current
252
+ );
253
+ }
254
+ );
255
+ var EditorCommandList = (0, import_react6.forwardRef)(
256
+ ({ children, ...rest }, ref) => {
257
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref, role: "listbox", ...rest, children });
199
258
  }
200
259
  );
201
- var EditorCommandList = import_cmdk.Command.List;
202
260
  EditorCommand.displayName = "EditorCommand";
261
+ EditorCommandList.displayName = "EditorCommandList";
203
262
 
204
263
  // src/headless/components/editor-command-item.tsx
205
264
  var import_react7 = require("react");
206
- var import_cmdk2 = require("cmdk");
207
265
  var import_react8 = require("@tiptap/react");
208
266
  var import_jotai5 = require("jotai");
209
267
  var import_jsx_runtime5 = require("react/jsx-runtime");
210
- var CommandItemAny = import_cmdk2.CommandItem;
211
- var CommandEmptyAny = import_cmdk2.CommandEmpty;
212
- var EditorCommandItem = (0, import_react7.forwardRef)(({ children, onCommand, ...rest }, ref) => {
268
+ var EditorCommandItem = (0, import_react7.forwardRef)(({ children, onCommand, value, className, ...rest }, ref) => {
213
269
  const { editor } = (0, import_react8.useCurrentEditor)();
214
- const range = (0, import_jotai5.useAtomValue)(rangeAtom);
270
+ const range = (0, import_jotai5.useAtomValue)(rangeAtom, { store: novelStore });
271
+ const query = (0, import_jotai5.useAtomValue)(queryAtom, { store: novelStore });
215
272
  if (!editor || !range) return null;
273
+ if (query && value) {
274
+ const searchText = value.toLowerCase();
275
+ const q = query.toLowerCase();
276
+ if (!searchText.includes(q)) return null;
277
+ }
216
278
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
217
- CommandItemAny,
279
+ "div",
218
280
  {
219
281
  ref,
282
+ role: "option",
283
+ className,
284
+ onClick: () => onCommand({ editor, range }),
220
285
  ...rest,
221
- onSelect: () => onCommand({ editor, range }),
222
286
  children
223
287
  }
224
288
  );
225
289
  });
226
290
  EditorCommandItem.displayName = "EditorCommandItem";
227
- var EditorCommandEmpty = CommandEmptyAny;
291
+ var EditorCommandEmpty = (0, import_react7.forwardRef)(
292
+ ({ children, ...rest }, ref) => {
293
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref, ...rest, children });
294
+ }
295
+ );
296
+ EditorCommandEmpty.displayName = "EditorCommandEmpty";
228
297
 
229
298
  // src/headless/extensions/index.ts
230
299
  var import_starter_kit = require("@tiptap/starter-kit");
@@ -674,10 +743,9 @@ var renderMentionSuggestion = () => {
674
743
  };
675
744
 
676
745
  // src/headless/extensions/slash-command.tsx
677
- var import_react12 = require("@tiptap/react");
678
746
  var import_suggestion = __toESM(require("@tiptap/suggestion"), 1);
679
747
  var import_core = require("@tiptap/core");
680
- var Command2 = import_core.Extension.create({
748
+ var Command = import_core.Extension.create({
681
749
  name: "slash-command",
682
750
  addOptions() {
683
751
  return {
@@ -695,7 +763,6 @@ var Command2 = import_core.Extension.create({
695
763
  (0, import_suggestion.default)({
696
764
  editor: this.editor,
697
765
  char: base.char ?? "/",
698
- // Only trigger slash command at start of line or after whitespace
699
766
  startOfLine: base.startOfLine ?? true,
700
767
  items: base.items ?? (() => ["/"]),
701
768
  command: (ctx) => {
@@ -703,76 +770,72 @@ var Command2 = import_core.Extension.create({
703
770
  ctx.props.command({ editor: ctx.editor, range: ctx.range });
704
771
  }
705
772
  },
706
- ...base
773
+ ...base,
774
+ render: () => {
775
+ return {
776
+ onStart: (props) => {
777
+ const { selection } = props.editor.state;
778
+ const parentNode = selection.$from.node(selection.$from.depth);
779
+ const blockType = parentNode.type.name;
780
+ if (blockType === "codeBlock") return false;
781
+ const { $from } = selection;
782
+ const marks = $from.marks();
783
+ if (marks.some((mark) => mark.type.name === "code" || mark.type.name === "link")) {
784
+ return false;
785
+ }
786
+ novelStore.set(queryAtom, props.query ?? "");
787
+ novelStore.set(rangeAtom, props.range ?? null);
788
+ novelStore.set(slashMenuOpenAtom, true);
789
+ const rect = typeof props.clientRect === "function" ? props.clientRect() : null;
790
+ novelStore.set(slashMenuRectAtom, rect);
791
+ },
792
+ onUpdate: (props) => {
793
+ novelStore.set(queryAtom, props.query ?? "");
794
+ novelStore.set(rangeAtom, props.range ?? null);
795
+ const rect = typeof props.clientRect === "function" ? props.clientRect() : null;
796
+ novelStore.set(slashMenuRectAtom, rect);
797
+ },
798
+ onKeyDown: ({ event }) => {
799
+ if (event.key === "Escape") {
800
+ novelStore.set(slashMenuOpenAtom, false);
801
+ return true;
802
+ }
803
+ if (["ArrowUp", "ArrowDown", "Enter"].includes(event.key)) {
804
+ const slashCommand = document.querySelector("#slash-command");
805
+ if (slashCommand) {
806
+ slashCommand.dispatchEvent(
807
+ new KeyboardEvent("keydown", {
808
+ key: event.key,
809
+ cancelable: true,
810
+ bubbles: true
811
+ })
812
+ );
813
+ return true;
814
+ }
815
+ }
816
+ return false;
817
+ },
818
+ onExit: () => {
819
+ novelStore.set(slashMenuOpenAtom, false);
820
+ novelStore.set(queryAtom, "");
821
+ novelStore.set(rangeAtom, null);
822
+ novelStore.set(slashMenuRectAtom, null);
823
+ }
824
+ };
825
+ }
707
826
  })
708
827
  ];
709
828
  }
710
829
  });
711
- var renderItems = (elementRef) => {
712
- let component = null;
713
- let container = null;
714
- const destroy = () => {
715
- component?.destroy();
716
- component = null;
717
- if (container) {
718
- container.remove();
719
- container = null;
720
- }
721
- };
722
- const updatePosition = (clientRect) => {
723
- if (!container || !clientRect) return;
724
- const top = Math.round(clientRect.bottom + 8);
725
- const left = Math.round(clientRect.left);
726
- container.style.top = `${top}px`;
727
- container.style.left = `${left}px`;
728
- };
729
- return {
730
- onStart: (props) => {
731
- const { selection } = props.editor.state;
732
- const parentNode = selection.$from.node(selection.$from.depth);
733
- const blockType = parentNode.type.name;
734
- if (blockType === "codeBlock") return false;
735
- const { $from } = selection;
736
- const marks = $from.marks();
737
- if (marks.some((mark) => mark.type.name === "code" || mark.type.name === "link")) {
738
- return false;
739
- }
740
- component = new import_react12.ReactRenderer(EditorCommandOut, {
741
- props: {
742
- query: props.query ?? "",
743
- range: props.range
744
- },
745
- editor: props.editor
746
- });
747
- container = document.createElement("div");
748
- container.style.position = "fixed";
749
- container.style.zIndex = "9999";
750
- container.style.minWidth = "240px";
751
- (elementRef?.current ?? document.body).appendChild(container);
752
- container.appendChild(component.element);
753
- const rect = typeof props.clientRect === "function" ? props.clientRect() : null;
754
- if (rect) updatePosition(rect);
755
- },
756
- onUpdate: (props) => {
757
- component?.updateProps({
758
- query: props.query ?? "",
759
- range: props.range
760
- });
761
- const rect = typeof props.clientRect === "function" ? props.clientRect() : null;
762
- if (rect) updatePosition(rect);
763
- },
764
- onKeyDown: ({ event }) => {
765
- if (event.key === "Escape") {
766
- destroy();
767
- return true;
768
- }
769
- return false;
770
- },
771
- onExit: () => {
772
- destroy();
773
- }
774
- };
775
- };
830
+ var renderItems = () => ({
831
+ onStart: () => {
832
+ },
833
+ onUpdate: () => {
834
+ },
835
+ onKeyDown: () => false,
836
+ onExit: () => {
837
+ }
838
+ });
776
839
  var createSuggestionItems = (items) => items;
777
840
  var handleCommandNavigation = (event) => {
778
841
  if (["ArrowUp", "ArrowDown", "Enter"].includes(event.key)) {