neuphlo-editor 1.2.0 → 1.3.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 (102) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +54 -0
  3. package/dist/{chunk-2CI535YH.js → chunk-RXTFDBJQ.js} +4 -1
  4. package/dist/chunk-RXTFDBJQ.js.map +1 -0
  5. package/dist/{chunk-7L2HLBM2.js → chunk-TPFBGHW3.js} +67 -53
  6. package/dist/chunk-TPFBGHW3.js.map +1 -0
  7. package/dist/headless/components/editor-bubble-item.d.ts +9 -0
  8. package/dist/headless/components/editor-bubble-item.js +29 -0
  9. package/dist/headless/components/editor-bubble.d.ts +9 -0
  10. package/dist/headless/components/editor-bubble.js +9 -0
  11. package/dist/headless/components/editor-command-item.d.ts +3 -0
  12. package/dist/headless/components/editor-command-item.js +18 -0
  13. package/dist/headless/components/editor-command.d.ts +10 -0
  14. package/dist/headless/components/editor-command.js +48 -0
  15. package/dist/headless/components/editor.d.ts +24 -0
  16. package/dist/headless/components/editor.js +13 -0
  17. package/dist/headless/extensions/CodeBlock/CodeBlock.d.ts +1 -0
  18. package/dist/headless/extensions/CodeBlock/CodeBlock.js +6 -0
  19. package/dist/headless/extensions/CodeBlock/index.d.ts +1 -0
  20. package/dist/headless/extensions/CodeBlock/index.js +1 -0
  21. package/dist/headless/extensions/Image/Image.d.ts +6 -0
  22. package/dist/headless/extensions/Image/Image.js +197 -0
  23. package/dist/headless/extensions/ImageBlock/ImageBlock.d.ts +21 -0
  24. package/dist/headless/extensions/ImageBlock/ImageBlock.js +240 -0
  25. package/dist/headless/extensions/ImageBlock/index.d.ts +2 -0
  26. package/dist/headless/extensions/ImageBlock/index.js +1 -0
  27. package/dist/headless/extensions/Link/Link.d.ts +2 -0
  28. package/dist/headless/extensions/Link/Link.js +40 -0
  29. package/dist/headless/extensions/Link/index.d.ts +1 -0
  30. package/dist/headless/extensions/Link/index.js +1 -0
  31. package/dist/headless/extensions/extension-kit.d.ts +5 -0
  32. package/dist/headless/extensions/extension-kit.js +29 -0
  33. package/dist/headless/extensions/index.d.ts +6 -0
  34. package/dist/headless/extensions/index.js +6 -0
  35. package/dist/headless/extensions/slash-command.d.ts +36 -0
  36. package/dist/headless/extensions/slash-command.js +105 -0
  37. package/dist/headless/index.cjs +4 -0
  38. package/dist/headless/index.cjs.map +1 -1
  39. package/dist/headless/index.d.cts +2 -1
  40. package/dist/headless/index.d.ts +2 -1
  41. package/dist/headless/index.js +3 -1
  42. package/dist/headless/utils/atoms.d.ts +7 -0
  43. package/dist/headless/utils/atoms.js +3 -0
  44. package/dist/headless/utils/store.d.ts +2 -0
  45. package/dist/headless/utils/store.js +4 -0
  46. package/dist/react/Editor.d.ts +27 -0
  47. package/dist/react/Editor.js +33 -0
  48. package/dist/react/index.cjs +62 -15
  49. package/dist/react/index.cjs.map +1 -1
  50. package/dist/react/index.d.cts +18 -5
  51. package/dist/react/index.d.ts +18 -5
  52. package/dist/react/index.js +55 -9
  53. package/dist/react/index.js.map +1 -1
  54. package/dist/react/menus/ImageBlock/ImageBlockLoading.d.ts +2 -0
  55. package/dist/react/menus/ImageBlock/ImageBlockLoading.js +6 -0
  56. package/dist/react/menus/ImageBlock/ImageBlockMenu.d.ts +7 -0
  57. package/dist/react/menus/ImageBlock/ImageBlockMenu.js +100 -0
  58. package/dist/react/menus/ImageBlock/ImageBlockView.d.ts +10 -0
  59. package/dist/react/menus/ImageBlock/ImageBlockView.js +44 -0
  60. package/dist/react/menus/ImageBlock/ImageBlockWidth.d.ts +6 -0
  61. package/dist/react/menus/ImageBlock/ImageBlockWidth.js +29 -0
  62. package/dist/react/menus/ImageBlock/ImageUploader.d.ts +7 -0
  63. package/dist/react/menus/ImageBlock/ImageUploader.js +63 -0
  64. package/dist/react/menus/ImageBlock/index.d.ts +4 -0
  65. package/dist/react/menus/ImageBlock/index.js +4 -0
  66. package/dist/react/menus/ImageMenu.d.ts +10 -0
  67. package/dist/react/menus/ImageMenu.js +79 -0
  68. package/dist/react/menus/ImagePopover.d.ts +8 -0
  69. package/dist/react/menus/ImagePopover.js +146 -0
  70. package/dist/react/menus/LinkPopover.d.ts +9 -0
  71. package/dist/react/menus/LinkPopover.js +113 -0
  72. package/dist/react/menus/MenuList.d.ts +10 -0
  73. package/dist/react/menus/MenuList.js +94 -0
  74. package/dist/react/menus/SlashMenu.d.ts +5 -0
  75. package/dist/react/menus/SlashMenu.js +79 -0
  76. package/dist/react/menus/TextMenu.d.ts +10 -0
  77. package/dist/react/menus/TextMenu.js +94 -0
  78. package/dist/react/menus/bubble-menu-extensions.d.ts +19 -0
  79. package/dist/react/menus/bubble-menu-extensions.js +17 -0
  80. package/dist/react/menus/index.d.ts +9 -0
  81. package/dist/react/menus/index.js +5 -0
  82. package/package.json +9 -11
  83. package/dist/chunk-2CI535YH.js.map +0 -1
  84. package/dist/chunk-3AD4UBZA.js +0 -1164
  85. package/dist/chunk-3AD4UBZA.js.map +0 -1
  86. package/dist/chunk-565WQB6C.js +0 -1226
  87. package/dist/chunk-565WQB6C.js.map +0 -1
  88. package/dist/chunk-6JRIOPA5.js +0 -1226
  89. package/dist/chunk-6JRIOPA5.js.map +0 -1
  90. package/dist/chunk-6OCCPGY7.js +0 -1172
  91. package/dist/chunk-6OCCPGY7.js.map +0 -1
  92. package/dist/chunk-7L2HLBM2.js.map +0 -1
  93. package/dist/chunk-EQHURHL2.js +0 -1226
  94. package/dist/chunk-EQHURHL2.js.map +0 -1
  95. package/dist/chunk-FW3S5SLJ.js +0 -366
  96. package/dist/chunk-FW3S5SLJ.js.map +0 -1
  97. package/dist/chunk-TGPB2LFU.js +0 -1056
  98. package/dist/chunk-TGPB2LFU.js.map +0 -1
  99. package/dist/chunk-ZWSMKLQY.js +0 -1043
  100. package/dist/chunk-ZWSMKLQY.js.map +0 -1
  101. package/dist/editor-DI3_SEDt.d.cts +0 -29
  102. package/dist/editor-DI3_SEDt.d.ts +0 -29
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Neuphlo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -228,6 +228,7 @@ const handleImageUpload = async (file: File): Promise<string> => {
228
228
  | `showImageMenu` | `boolean` | `true` | Show image controls bubble menu |
229
229
  | `showSlashMenu` | `boolean` | `true` | Show slash command menu |
230
230
  | `extensions` | `Extension[]` | `[]` | Additional Tiptap extensions |
231
+ | `bubbleMenuExtras` | `{ text?: BubbleMenuExtra \| BubbleMenuExtra[]; image?: BubbleMenuExtra \| BubbleMenuExtra[] }` | `undefined` | Append custom UI to bubble menus |
231
232
  | `uploadImage` | `(file: File) => Promise<string>` | `undefined` | Image upload handler |
232
233
  | `onUpdate` | `({ editor }) => void` | `undefined` | Called when content changes |
233
234
  | `onCreate` | `({ editor }) => void` | `undefined` | Called when editor is created |
@@ -253,6 +254,59 @@ const handleImageUpload = async (file: File): Promise<string> => {
253
254
  />
254
255
  ```
255
256
 
257
+ ### Bubble Menu Extras
258
+
259
+ Use the `bubbleMenuExtras` prop when you need to sprinkle in project-specific controls (AI helpers, analytics buttons, etc.) without editing this package.
260
+
261
+ ```tsx
262
+ import type { Editor } from "@tiptap/react"
263
+
264
+ const bubbleMenuExtras = {
265
+ text: {
266
+ align: "start", // show on the left side of the menu
267
+ render: (editor: Editor) => (
268
+ <button
269
+ type="button"
270
+ className="nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon"
271
+ onMouseDown={(e) => e.preventDefault()}
272
+ onClick={() => {
273
+ if (editor.state.selection.empty) return
274
+ const note = window.prompt("Add note", "Needs review")
275
+ if (!note) return
276
+ const { to } = editor.state.selection
277
+ editor.chain().focus().insertContentAt(to, ` [Note: ${note}]`).run()
278
+ }}
279
+ >
280
+ Add note
281
+ </button>
282
+ ),
283
+ },
284
+ image: {
285
+ // Default align is "end" (right side). You can pass an array for multiple buttons.
286
+ render: (editor: Editor) => (
287
+ <button
288
+ type="button"
289
+ className="nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon"
290
+ onMouseDown={(e) => e.preventDefault()}
291
+ onClick={() =>
292
+ editor
293
+ .chain()
294
+ .focus()
295
+ .updateAttributes("image", { align: "left" })
296
+ .run()
297
+ }
298
+ >
299
+ Pin left
300
+ </button>
301
+ ),
302
+ },
303
+ }
304
+
305
+ <Editor bubbleMenuExtras={bubbleMenuExtras} />
306
+ ```
307
+
308
+ Each render callback receives the live Tiptap editor so you can check selection state, trigger commands, or early-return `null` to hide your custom control. The optional `align` flag lets you position the control on the left (`"start"`) or right (`"end"`, default) side of the bubble menu.
309
+
256
310
  ## Styling
257
311
 
258
312
  ### Basic Styling
@@ -600,6 +600,7 @@ import { useCurrentEditor as useCurrentEditor3 } from "@tiptap/react";
600
600
  import { useAtomValue } from "jotai";
601
601
  import { jsx as jsx5 } from "react/jsx-runtime";
602
602
  var CommandItemAny = CommandItem;
603
+ var CommandEmptyAny = CommandEmpty;
603
604
  var EditorCommandItem = forwardRef4(({ children, onCommand, ...rest }, ref) => {
604
605
  const { editor } = useCurrentEditor3();
605
606
  const range = useAtomValue(rangeAtom);
@@ -615,6 +616,7 @@ var EditorCommandItem = forwardRef4(({ children, onCommand, ...rest }, ref) => {
615
616
  );
616
617
  });
617
618
  EditorCommandItem.displayName = "EditorCommandItem";
619
+ var EditorCommandEmpty = CommandEmptyAny;
618
620
 
619
621
  // src/headless/extensions/index.ts
620
622
  import { StarterKit } from "@tiptap/starter-kit";
@@ -1024,6 +1026,7 @@ export {
1024
1026
  EditorCommand,
1025
1027
  EditorCommandList,
1026
1028
  EditorCommandItem,
1029
+ EditorCommandEmpty,
1027
1030
  CodeBlock,
1028
1031
  Link,
1029
1032
  ImageBlock,
@@ -1035,4 +1038,4 @@ export {
1035
1038
  handleCommandNavigation,
1036
1039
  useCurrentEditor4 as useCurrentEditor
1037
1040
  };
1038
- //# sourceMappingURL=chunk-2CI535YH.js.map
1041
+ //# sourceMappingURL=chunk-RXTFDBJQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/menus/ImageBlock/ImageBlockWidth.tsx","../src/react/menus/ImageBlock/ImageBlockMenu.tsx","../src/react/menus/ImageBlock/ImageBlockLoading.tsx","../src/react/menus/ImageBlock/ImageUploader.tsx","../src/react/menus/ImageBlock/ImageBlockView.tsx","../src/headless/index.ts","../src/headless/components/editor.tsx","../src/headless/utils/store.ts","../src/headless/components/editor-bubble.tsx","../src/headless/components/editor-bubble-item.tsx","../src/headless/components/editor-command.tsx","../src/headless/utils/atoms.ts","../src/headless/components/editor-command-item.tsx","../src/headless/extensions/index.ts","../src/headless/extensions/CodeBlock/CodeBlock.ts","../src/headless/extensions/Link/Link.ts","../src/headless/extensions/ImageBlock/ImageBlock.ts","../src/headless/extensions/slash-command.tsx"],"sourcesContent":["import { memo, useCallback, useEffect, useState } from \"react\"\n\nexport type ImageBlockWidthProps = {\n onChange: (value: number) => void\n value: number\n}\n\nexport const ImageBlockWidth = memo(\n ({ onChange, value }: ImageBlockWidthProps) => {\n const [currentValue, setCurrentValue] = useState(value)\n\n useEffect(() => {\n setCurrentValue(value)\n }, [value])\n\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const nextValue = parseInt(e.target.value)\n onChange(nextValue)\n setCurrentValue(nextValue)\n },\n [onChange]\n )\n\n return (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n minWidth: 140,\n }}\n >\n <span style={{ fontSize: \"12px\", whiteSpace: \"nowrap\", width: 40 }}>\n {currentValue}%\n </span>\n <input\n type=\"range\"\n min=\"25\"\n max=\"100\"\n step=\"25\"\n value={currentValue}\n onChange={handleChange}\n onMouseDown={(e) => e.stopPropagation()}\n style={{\n flex: 1,\n height: 3,\n borderRadius: 9999,\n appearance: \"none\",\n background: \"linear-gradient(to right, #333 0%, #333 \" + currentValue + \"%, #ddd \" + currentValue + \"%, #ddd 100%)\",\n outline: \"none\",\n cursor: \"pointer\",\n }}\n className=\"nph-image-width-slider\"\n />\n </div>\n )\n }\n)\n\nImageBlockWidth.displayName = \"ImageBlockWidth\"\n\nexport default ImageBlockWidth\n","import { Editor } from \"@tiptap/react\"\nimport { BubbleMenu } from \"@tiptap/react/menus\"\nimport { useCallback, useRef, useState, useEffect } from \"react\"\nimport { ImageBlockWidth } from \"./ImageBlockWidth\"\nimport {\n IconAlignLeft,\n IconAlignCenter,\n IconAlignRight,\n IconTrash,\n} from \"@tabler/icons-react\"\n\nexport type ImageBlockMenuProps = {\n editor: Editor\n appendTo?: React.RefObject<HTMLElement | null>\n}\n\nexport const ImageBlockMenu = ({ editor, appendTo }: ImageBlockMenuProps) => {\n const menuRef = useRef<HTMLDivElement>(null)\n const [align, setAlign] = useState<\"left\" | \"center\" | \"right\">(\"center\")\n const [width, setWidth] = useState<number>(100)\n\n useEffect(() => {\n if (!editor) return\n const update = () => {\n if (!editor.isActive(\"imageBlock\")) return\n const attrs = editor.getAttributes(\"imageBlock\")\n setAlign(attrs.align || \"center\")\n const widthStr = attrs.width || \"100%\"\n setWidth(parseInt(widthStr) || 100)\n }\n update()\n editor.on(\"selectionUpdate\", update)\n editor.on(\"transaction\", update)\n return () => {\n editor.off(\"selectionUpdate\", update)\n editor.off(\"transaction\", update)\n }\n }, [editor])\n\n const getReferenceClientRect = useCallback(() => {\n if (!editor) return new DOMRect(-1000, -1000, 0, 0)\n\n const { view } = editor\n const { state } = view\n const { selection } = state\n\n // Get the node at the current selection\n const node = selection instanceof (window as any).ProseMirror?.state?.NodeSelection\n ? (selection as any).node\n : null\n\n if (node && node.type.name === \"imageBlock\") {\n const nodePos = (selection as any).from\n const domNode = view.nodeDOM(nodePos)\n if (domNode && domNode instanceof HTMLElement) {\n return domNode.getBoundingClientRect()\n }\n }\n\n // Fallback: try to find the image block element\n const imageBlockElements = document.querySelectorAll('[data-node-view-wrapper]')\n for (const el of Array.from(imageBlockElements)) {\n if (el.querySelector(\"img\")) {\n return el.getBoundingClientRect()\n }\n }\n\n return new DOMRect(-1000, -1000, 0, 0)\n }, [editor])\n\n const shouldShow = useCallback(() => {\n if (!editor) return false\n const isActive = editor.isActive(\"imageBlock\")\n if (!isActive) return false\n\n // Check if it's a node selection\n const { state } = editor\n const { selection } = state\n const isNodeSelection = selection.constructor.name === \"NodeSelection\"\n\n return isNodeSelection\n }, [editor])\n\n const onAlignImageLeft = useCallback(() => {\n editor\n .chain()\n .focus(undefined, { scrollIntoView: false })\n .setImageBlockAlign(\"left\")\n .run()\n }, [editor])\n\n const onAlignImageCenter = useCallback(() => {\n editor\n .chain()\n .focus(undefined, { scrollIntoView: false })\n .setImageBlockAlign(\"center\")\n .run()\n }, [editor])\n\n const onAlignImageRight = useCallback(() => {\n editor\n .chain()\n .focus(undefined, { scrollIntoView: false })\n .setImageBlockAlign(\"right\")\n .run()\n }, [editor])\n\n const onWidthChange = useCallback(\n (value: number) => {\n editor\n .chain()\n .focus(undefined, { scrollIntoView: false })\n .setImageBlockWidth(value)\n .run()\n },\n [editor]\n )\n\n const onRemoveImage = useCallback(() => {\n editor.chain().focus(undefined, { scrollIntoView: false }).deleteSelection().run()\n }, [editor])\n\n return (\n <BubbleMenu\n editor={editor}\n shouldShow={shouldShow}\n updateDelay={0}\n >\n <div className=\"bubble-menu\" ref={menuRef}>\n <button\n type=\"button\"\n className={`nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon${align === \"left\" ? \" is-active\" : \"\"}`}\n title=\"Align left\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={onAlignImageLeft}\n >\n <IconAlignLeft size={16} />\n </button>\n <button\n type=\"button\"\n className={`nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon${align === \"center\" ? \" is-active\" : \"\"}`}\n title=\"Align center\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={onAlignImageCenter}\n >\n <IconAlignCenter size={16} />\n </button>\n <button\n type=\"button\"\n className={`nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon${align === \"right\" ? \" is-active\" : \"\"}`}\n title=\"Align right\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={onAlignImageRight}\n >\n <IconAlignRight size={16} />\n </button>\n <div\n className=\"nph-link-popover__divider\"\n style={{ margin: \"0 4px\" }}\n />\n <ImageBlockWidth onChange={onWidthChange} value={width} />\n <div\n className=\"nph-link-popover__divider\"\n style={{ margin: \"0 4px\" }}\n />\n <button\n type=\"button\"\n className=\"nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon\"\n title=\"Remove image\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={onRemoveImage}\n >\n <IconTrash size={16} />\n </button>\n </div>\n </BubbleMenu>\n )\n}\n\nexport default ImageBlockMenu\n","import { IconLoader2 } from \"@tabler/icons-react\"\n\nexport const ImageBlockLoading = () => {\n return (\n <div className=\"nph-image-block-loading\">\n <div className=\"nph-image-block-loading__overlay\">\n <div className=\"nph-image-block-loading__content\">\n <IconLoader2 size={24} className=\"nph-image-block-loading__spinner\" />\n <p className=\"nph-image-block-loading__text\">Uploading image...</p>\n </div>\n </div>\n <div className=\"nph-image-block-loading__placeholder\" />\n </div>\n )\n}\n\nexport default ImageBlockLoading\n","import { IconPhoto, IconUpload } from \"@tabler/icons-react\"\nimport { ChangeEvent, useCallback, useRef, useState, DragEvent } from \"react\"\nimport { Editor } from \"@tiptap/react\"\nimport { ImageBlockLoading } from \"./ImageBlockLoading\"\n\nexport type ImageUploaderProps = {\n onUpload: (url: string) => void\n editor: Editor\n}\n\nexport const ImageUploader = ({ onUpload, editor }: ImageUploaderProps) => {\n const [loading, setLoading] = useState(false)\n const [draggedInside, setDraggedInside] = useState(false)\n const fileInputRef = useRef<HTMLInputElement>(null)\n\n const uploadFile = useCallback(\n async (file: File) => {\n setLoading(true)\n try {\n // Get the upload handler from the editor\n const imageExtension = editor.extensionManager.extensions.find(\n (ext) => ext.name === \"imageBlock\"\n )\n const uploadImage = (imageExtension?.options as any)?.uploadImage\n\n if (uploadImage) {\n const url = await uploadImage(file)\n onUpload(url)\n } else {\n console.error(\"No uploadImage handler provided\")\n }\n } catch (error) {\n console.error(\"Failed to upload image:\", error)\n } finally {\n setLoading(false)\n }\n },\n [editor, onUpload]\n )\n\n const handleUploadClick = useCallback(() => {\n fileInputRef.current?.click()\n }, [])\n\n const onFileChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0]\n if (file) {\n uploadFile(file)\n }\n },\n [uploadFile]\n )\n\n const onDrop = useCallback(\n (e: DragEvent<HTMLDivElement>) => {\n e.preventDefault()\n e.stopPropagation()\n setDraggedInside(false)\n\n const file = e.dataTransfer.files[0]\n if (file && /image/i.test(file.type)) {\n uploadFile(file)\n }\n },\n [uploadFile]\n )\n\n const onDragEnter = useCallback((e: DragEvent<HTMLDivElement>) => {\n e.preventDefault()\n e.stopPropagation()\n setDraggedInside(true)\n }, [])\n\n const onDragLeave = useCallback((e: DragEvent<HTMLDivElement>) => {\n e.preventDefault()\n e.stopPropagation()\n setDraggedInside(false)\n }, [])\n\n if (loading) {\n return <ImageBlockLoading />\n }\n\n return (\n <div\n className={`nph-image-uploader${draggedInside ? \" nph-image-uploader--dragging\" : \"\"}`}\n onDrop={onDrop}\n onDragOver={onDragEnter}\n onDragLeave={onDragLeave}\n contentEditable={false}\n >\n <IconPhoto size={48} className=\"nph-image-uploader__icon\" />\n <div className=\"nph-image-uploader__content\">\n <div className=\"nph-image-uploader__text\">\n {draggedInside ? \"Drop image here\" : \"Drag and drop or\"}\n </div>\n <div>\n <button\n type=\"button\"\n disabled={draggedInside}\n onClick={handleUploadClick}\n className=\"nph-btn nph-btn-ghost nph-btn-sm nph-image-uploader__button\"\n >\n <IconUpload size={16} />\n Upload an image\n </button>\n </div>\n </div>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\".jpg,.jpeg,.png,.webp,.gif\"\n onChange={onFileChange}\n className=\"nph-image-uploader__input\"\n />\n </div>\n )\n}\n\nexport default ImageUploader\n","import { Node } from \"@tiptap/pm/model\"\nimport { Editor, NodeViewWrapper } from \"@tiptap/react\"\nimport { useCallback, useRef } from \"react\"\nimport { ImageBlockMenu } from \"./ImageBlockMenu\"\nimport { ImageUploader } from \"./ImageUploader\"\nimport { ImageBlockLoading } from \"./ImageBlockLoading\"\n\ninterface ImageBlockViewProps {\n editor: Editor\n getPos: () => number\n node: Node\n updateAttributes: (attrs: Record<string, any>) => void\n}\n\nexport const ImageBlockView = (props: ImageBlockViewProps) => {\n const { editor, getPos, node, updateAttributes } = props as ImageBlockViewProps & {\n node: Node & {\n attrs: {\n src: string\n width: string\n align: \"left\" | \"center\" | \"right\"\n alt?: string\n loading?: boolean\n }\n }\n }\n const imageWrapperRef = useRef<HTMLDivElement>(null)\n const { src, width, align, alt, loading } = node.attrs\n\n const handleUpload = useCallback(\n (url: string) => {\n updateAttributes({ src: url, loading: false })\n },\n [updateAttributes]\n )\n\n const onClick = useCallback(() => {\n editor.commands.setNodeSelection(getPos())\n }, [getPos, editor.commands])\n\n // Calculate wrapper class based on alignment\n const getWrapperStyle = (): React.CSSProperties => {\n const baseStyle: React.CSSProperties = {\n width: width || \"100%\",\n maxWidth: \"100%\",\n }\n\n if (align === \"left\") {\n return { ...baseStyle, marginLeft: 0, marginRight: \"auto\" }\n } else if (align === \"right\") {\n return { ...baseStyle, marginLeft: \"auto\", marginRight: 0 }\n } else {\n return { ...baseStyle, marginLeft: \"auto\", marginRight: \"auto\" }\n }\n }\n\n // Show uploader if no src\n if (!src || src === \"\") {\n return (\n <NodeViewWrapper>\n <div style={getWrapperStyle()}>\n <div ref={imageWrapperRef}>\n <ImageUploader onUpload={handleUpload} editor={editor} />\n </div>\n </div>\n </NodeViewWrapper>\n )\n }\n\n // Show loading placeholder\n if (loading) {\n return (\n <NodeViewWrapper>\n <div style={getWrapperStyle()}>\n <div ref={imageWrapperRef}>\n <ImageBlockLoading />\n </div>\n </div>\n </NodeViewWrapper>\n )\n }\n\n // Show the actual image\n return (\n <NodeViewWrapper>\n <div style={getWrapperStyle()}>\n <div contentEditable={false} ref={imageWrapperRef} style={{ position: \"relative\" }}>\n <img\n src={src}\n alt={alt || \"\"}\n onClick={onClick}\n className=\"nph-image-block\"\n />\n </div>\n </div>\n <ImageBlockMenu editor={editor} appendTo={imageWrapperRef} />\n </NodeViewWrapper>\n )\n}\n\nexport default ImageBlockView\n","export { useCurrentEditor as useEditor } from \"@tiptap/react\"\n\nexport {\n EditorRoot,\n EditorContent,\n type EditorContentProps,\n} from \"./components/editor\"\n\nexport { EditorBubble } from \"./components/editor-bubble\"\nexport { EditorBubbleItem } from \"./components/editor-bubble-item\"\nexport {\n EditorCommand,\n EditorCommandList,\n EditorCommandOut,\n} from \"./components/editor-command\"\nexport {\n EditorCommandItem,\n EditorCommandEmpty,\n} from \"./components/editor-command-item\"\n\nexport { Placeholder, StarterKit } from \"./extensions\"\nexport {\n Command as SlashCommand,\n renderItems as renderSlashCommandItems,\n createSuggestionItems,\n handleCommandNavigation,\n} from \"./extensions/slash-command\"\n// Path without extension to satisfy TS/tsup\n// (the file is at ./extensions/slash-command.tsx)\n// eslint-disable-next-line\n","import { EditorProvider } from \"@tiptap/react\"\nimport type { EditorProviderProps } from \"@tiptap/react\"\nimport { forwardRef } from \"react\"\nimport type { FC, ReactNode } from \"react\"\nimport { Provider } from \"jotai\"\nimport { novelStore } from \"../utils/store\"\n\nexport interface EditorRootProps {\n readonly children: ReactNode\n}\n\nexport const EditorRoot: FC<EditorRootProps> = ({ children }) => {\n return (\n <Provider store={novelStore as any}>\n {children as any}\n </Provider>\n )\n}\n\nexport type EditorContentProps = EditorProviderProps & {\n readonly children?: ReactNode\n readonly className?: string\n readonly initialContent?: any\n}\n\nexport const EditorContent = forwardRef<HTMLDivElement, EditorContentProps>(\n ({ className, children, initialContent, content, ...rest }, ref) => {\n const effectiveContent = content ?? initialContent\n return (\n <div ref={ref} className={className}>\n <EditorProvider {...rest} content={effectiveContent}>\n {children}\n </EditorProvider>\n </div>\n )\n }\n)\n\nEditorContent.displayName = \"EditorContent\"\n","import { createStore } from \"jotai\";\n\n// biome-ignore lint/suspicious/noExplicitAny: store is opaque to consumers\nexport const novelStore: any = createStore();\nexport * from \"jotai\";\n\n","import { useCurrentEditor } from \"@tiptap/react\";\nimport { BubbleMenu as BubbleMenuReact } from \"@tiptap/react/menus\";\nimport type { BubbleMenuProps } from \"@tiptap/react/menus\";\nimport type { ReactNode } from \"react\";\n\ntype ForwardedBubbleProps = Omit<BubbleMenuProps, \"editor\" | \"children\" | \"className\">;\n\nexport interface EditorBubbleProps extends ForwardedBubbleProps {\n readonly className?: string;\n readonly children: ReactNode;\n}\n\nexport function EditorBubble({ className, children, ...rest }: EditorBubbleProps) {\n const { editor } = useCurrentEditor();\n if (!editor) return null;\n\n return (\n <BubbleMenuReact editor={editor} {...rest}>\n <div className={className}>{children}</div>\n </BubbleMenuReact>\n );\n}\n","import { forwardRef, isValidElement, cloneElement } from \"react\"\nimport type { ComponentPropsWithoutRef, ReactElement, ReactNode } from \"react\"\nimport { useCurrentEditor } from \"@tiptap/react\"\nimport type { Editor as TiptapEditor } from \"@tiptap/react\"\n\ninterface EditorBubbleItemProps {\n readonly children: ReactNode\n readonly asChild?: boolean\n readonly onSelect?: (editor: TiptapEditor) => void\n}\n\nexport const EditorBubbleItem = forwardRef<\n HTMLDivElement,\n EditorBubbleItemProps & Omit<ComponentPropsWithoutRef<\"div\">, \"onSelect\">\n>(({ children, asChild, onSelect, ...rest }, ref) => {\n const { editor } = useCurrentEditor()\n\n if (!editor) return null\n\n const handleClick = (e: React.MouseEvent) => {\n e.preventDefault()\n onSelect?.(editor)\n }\n\n if (asChild && isValidElement(children)) {\n const child = children as ReactElement<any>\n const childOnClick = (child.props as any)?.onClick as\n | ((e: any) => void)\n | undefined\n const mergedOnClick = (e: any) => {\n childOnClick?.(e)\n if (!e?.defaultPrevented) onSelect?.(editor)\n }\n\n return cloneElement(child, {\n ...rest,\n ref: (child as any).ref ?? ref,\n onClick: mergedOnClick,\n })\n }\n\n return (\n <div ref={ref} {...rest} onClick={handleClick}>\n {children}\n </div>\n )\n})\n\nEditorBubbleItem.displayName = \"EditorBubbleItem\"\n\nexport default EditorBubbleItem\n","import { useAtom, useSetAtom } from \"jotai\"\nimport { useEffect, forwardRef } from \"react\"\nimport { Command } from \"cmdk\"\nimport { queryAtom, rangeAtom } from \"../utils/atoms\"\nimport { novelStore } from \"../utils/store\"\nimport type { FC } from \"react\"\nimport type { Range } from \"@tiptap/core\"\nimport tunnel from \"tunnel-rat\"\n\nconst commandTunnel: any = (tunnel as any)()\n\ninterface EditorCommandOutProps {\n readonly query: string\n readonly range: Range\n}\n\nexport const EditorCommandOut: FC<EditorCommandOutProps> = ({\n query,\n range,\n}) => {\n const setQuery = useSetAtom(queryAtom, { store: novelStore })\n const setRange = useSetAtom(rangeAtom, { store: novelStore })\n\n useEffect(() => {\n setQuery(query)\n }, [query, setQuery])\n\n useEffect(() => {\n setRange(range)\n }, [range, setRange])\n\n useEffect(() => {\n const navigationKeys = [\"ArrowUp\", \"ArrowDown\", \"Enter\"]\n const onKeyDown = (e: KeyboardEvent) => {\n if (navigationKeys.includes(e.key)) {\n e.preventDefault()\n const commandRef = document.querySelector(\"#slash-command\")\n\n if (commandRef)\n commandRef.dispatchEvent(\n new KeyboardEvent(\"keydown\", {\n key: e.key,\n cancelable: true,\n bubbles: true,\n })\n )\n\n return false\n }\n }\n document.addEventListener(\"keydown\", onKeyDown)\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown)\n }\n }, [])\n\n return <commandTunnel.Out />\n}\n\nconst CommandAny: any = Command\nexport const EditorCommand = forwardRef<HTMLDivElement, any>(\n ({ children, className, ...rest }, ref) => {\n const [query, setQuery] = useAtom(queryAtom)\n\n return (\n <commandTunnel.In>\n <CommandAny\n ref={ref}\n onKeyDown={(e: any) => {\n e.stopPropagation()\n }}\n id=\"slash-command\"\n className={className}\n {...rest}\n >\n <CommandAny.Input\n value={query}\n onValueChange={setQuery}\n style={{ display: \"none\" }}\n />\n {children}\n </CommandAny>\n </commandTunnel.In>\n )\n }\n)\nexport const EditorCommandList: any = Command.List\n\nEditorCommand.displayName = \"EditorCommand\"\n","import { atom } from \"jotai\"\nimport type { Range } from \"@tiptap/core\"\n\nexport const queryAtom = atom(\"\")\nexport const rangeAtom = atom<Range | null>(null)\n\n","import { forwardRef } from \"react\"\nimport { CommandEmpty, CommandItem } from \"cmdk\"\nimport { useCurrentEditor } from \"@tiptap/react\"\nimport { useAtomValue } from \"jotai\"\nimport { rangeAtom } from \"../utils/atoms\"\nimport type { Editor, Range } from \"@tiptap/core\"\n\ninterface EditorCommandItemProps {\n readonly onCommand: ({\n editor,\n range,\n }: {\n editor: Editor\n range: Range\n }) => void\n}\n\nconst CommandItemAny: any = CommandItem\nconst CommandEmptyAny: any = CommandEmpty\n\nexport const EditorCommandItem = forwardRef<\n HTMLDivElement,\n EditorCommandItemProps & any\n>(({ children, onCommand, ...rest }, ref) => {\n const { editor } = useCurrentEditor()\n const range = useAtomValue(rangeAtom)\n\n if (!editor || !range) return null\n\n return (\n <CommandItemAny\n ref={ref}\n {...(rest as any)}\n onSelect={() => onCommand({ editor, range })}\n >\n {children}\n </CommandItemAny>\n )\n})\n\nEditorCommandItem.displayName = \"EditorCommandItem\"\n\nexport const EditorCommandEmpty: any = CommandEmptyAny\n\nexport default EditorCommandItem\n","export { StarterKit } from \"@tiptap/starter-kit\"\nexport { Placeholder } from \"@tiptap/extension-placeholder\"\n\n// Custom\nexport { CodeBlock } from \"./CodeBlock\"\nexport { Link } from \"./Link\"\nexport { ImageBlock } from \"./ImageBlock\"\nexport type { ImageBlockOptions } from \"./ImageBlock\"\n","import { CodeBlockLowlight } from \"@tiptap/extension-code-block-lowlight\"\nimport { all, createLowlight } from \"lowlight\"\n\nconst lowlight = createLowlight(all)\n\nexport const CodeBlock = CodeBlockLowlight.configure({\n lowlight,\n})\n","import { mergeAttributes } from \"@tiptap/core\"\nimport TiptapLink from \"@tiptap/extension-link\"\nimport { Plugin } from \"@tiptap/pm/state\"\nimport { EditorView } from \"@tiptap/pm/view\"\n\nexport const Link = TiptapLink.extend({\n inclusive: false,\n\n parseHTML() {\n return [\n {\n tag: 'a[href]:not([data-type=\"button\"]):not([href *= \"javascript:\" i])',\n },\n ]\n },\n\n renderHTML({ HTMLAttributes }: any) {\n return [\n \"a\",\n mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {\n class: \"link\",\n }),\n 0,\n ]\n },\n\n addProseMirrorPlugins() {\n const { editor } = this\n\n return [\n ...(((this as any).parent?.() as any[]) || []),\n new Plugin({\n props: {\n handleKeyDown: (view: EditorView, event: KeyboardEvent) => {\n const { selection } = editor.state\n\n if (event.key === \"Escape\" && selection.empty !== true) {\n editor.commands.focus(selection.to, { scrollIntoView: false })\n }\n\n return false\n },\n },\n }),\n ]\n },\n})\n\nexport default Link\n","import { mergeAttributes, Range } from \"@tiptap/core\"\nimport { Image as TiptapImage } from \"@tiptap/extension-image\"\nimport { ReactNodeViewRenderer } from \"@tiptap/react\"\nimport { Plugin, PluginKey } from \"@tiptap/pm/state\"\nimport { EditorView } from \"@tiptap/pm/view\"\n\nexport interface ImageBlockOptions {\n uploadImage?: (file: File) => Promise<string>\n}\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n imageBlock: {\n setImageBlock: (attributes: { src: string }) => ReturnType\n setImageBlockAt: (attributes: {\n src: string\n pos: number | Range\n }) => ReturnType\n setImageBlockAlign: (align: \"left\" | \"center\" | \"right\") => ReturnType\n setImageBlockWidth: (width: number) => ReturnType\n }\n }\n}\n\nexport const ImageBlock = TiptapImage.extend<ImageBlockOptions>({\n name: \"imageBlock\",\n\n group: \"block\",\n\n defining: true,\n\n isolating: true,\n\n addOptions() {\n return {\n ...this.parent?.(),\n uploadImage: undefined,\n inline: false,\n }\n },\n\n addAttributes() {\n return {\n src: {\n default: \"\",\n parseHTML: (element) => element.getAttribute(\"src\"),\n renderHTML: (attributes) => ({\n src: attributes.src,\n }),\n },\n width: {\n default: \"100%\",\n parseHTML: (element) => element.getAttribute(\"data-width\"),\n renderHTML: (attributes) => ({\n \"data-width\": attributes.width,\n }),\n },\n align: {\n default: \"center\",\n parseHTML: (element) => element.getAttribute(\"data-align\"),\n renderHTML: (attributes) => ({\n \"data-align\": attributes.align,\n }),\n },\n alt: {\n default: undefined,\n parseHTML: (element) => element.getAttribute(\"alt\"),\n renderHTML: (attributes) => ({\n alt: attributes.alt,\n }),\n },\n loading: {\n default: false,\n parseHTML: () => false,\n renderHTML: () => ({}),\n },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'img[src]:not([src^=\"data:\"])',\n getAttrs: (element) => {\n const el = element as HTMLElement\n return {\n src: el.getAttribute(\"src\"),\n alt: el.getAttribute(\"alt\"),\n width: el.getAttribute(\"data-width\") || \"100%\",\n align: el.getAttribute(\"data-align\") || \"center\",\n }\n },\n },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\"img\", mergeAttributes(HTMLAttributes)]\n },\n\n addCommands() {\n return {\n setImageBlock:\n (attrs) =>\n ({ commands }) => {\n return commands.insertContent({\n type: \"imageBlock\",\n attrs: { src: attrs.src },\n })\n },\n\n setImageBlockAt:\n (attrs) =>\n ({ commands }) => {\n return commands.insertContentAt(attrs.pos, {\n type: \"imageBlock\",\n attrs: { src: attrs.src },\n })\n },\n\n setImageBlockAlign:\n (align) =>\n ({ commands }) =>\n commands.updateAttributes(\"imageBlock\", { align }),\n\n setImageBlockWidth:\n (width) =>\n ({ commands }) =>\n commands.updateAttributes(\"imageBlock\", {\n width: `${Math.max(0, Math.min(100, width))}%`,\n }),\n }\n },\n\n addNodeView() {\n // We'll import this dynamically to avoid circular dependencies\n // The view will be registered from the React side\n return ReactNodeViewRenderer(\n require(\"../../../react/menus/ImageBlock/ImageBlockView\").ImageBlockView\n )\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey(\"imageBlockDrop\"),\n props: {\n handleDOMEvents: {\n drop: (view: EditorView, event: DragEvent) => {\n const hasFiles =\n event.dataTransfer &&\n event.dataTransfer.files &&\n event.dataTransfer.files.length\n\n if (!hasFiles) {\n return false\n }\n\n const images = Array.from(event.dataTransfer.files).filter(\n (file) => /image/i.test(file.type)\n )\n\n if (images.length === 0) {\n return false\n }\n\n event.preventDefault()\n\n const { schema } = view.state\n const coordinates = view.posAtCoords({\n left: event.clientX,\n top: event.clientY,\n })\n\n if (!coordinates) return false\n\n images.forEach(async (image) => {\n if (this.options.uploadImage) {\n try {\n // Insert placeholder first\n const placeholderNode = schema.nodes.imageBlock.create({\n src: \"\",\n loading: true,\n })\n const placeholderTr = view.state.tr.insert(\n coordinates.pos,\n placeholderNode\n )\n view.dispatch(placeholderTr)\n\n // Upload and replace\n const url = await this.options.uploadImage(image)\n const node = schema.nodes.imageBlock.create({ src: url })\n\n // Find and replace the placeholder\n const currentState = view.state\n let foundPos = -1\n currentState.doc.descendants((node, pos) => {\n if (\n node.type.name === \"imageBlock\" &&\n node.attrs.loading\n ) {\n foundPos = pos\n return false\n }\n })\n\n if (foundPos !== -1) {\n const transaction = view.state.tr.replaceWith(\n foundPos,\n foundPos + 1,\n node\n )\n view.dispatch(transaction)\n }\n } catch (error) {\n console.error(\"Failed to upload image:\", error)\n // Remove placeholder on error\n const currentState = view.state\n let foundPos = -1\n currentState.doc.descendants((node, pos) => {\n if (\n node.type.name === \"imageBlock\" &&\n node.attrs.loading\n ) {\n foundPos = pos\n return false\n }\n })\n\n if (foundPos !== -1) {\n const transaction = view.state.tr.delete(\n foundPos,\n foundPos + 1\n )\n view.dispatch(transaction)\n }\n }\n }\n })\n\n return true\n },\n paste: (view: EditorView, event: ClipboardEvent) => {\n const hasFiles =\n event.clipboardData &&\n event.clipboardData.files &&\n event.clipboardData.files.length\n\n if (!hasFiles) {\n return false\n }\n\n const images = Array.from(event.clipboardData.files).filter(\n (file) => /image/i.test(file.type)\n )\n\n if (images.length === 0) {\n return false\n }\n\n event.preventDefault()\n\n images.forEach(async (image) => {\n if (this.options.uploadImage) {\n try {\n // Insert placeholder first\n const placeholderNode =\n view.state.schema.nodes.imageBlock.create({\n src: \"\",\n loading: true,\n })\n view.dispatch(\n view.state.tr.replaceSelectionWith(placeholderNode)\n )\n\n // Upload and replace\n const url = await this.options.uploadImage(image)\n const node = view.state.schema.nodes.imageBlock.create({\n src: url,\n })\n\n // Find and replace the placeholder\n const currentState = view.state\n let foundPos = -1\n currentState.doc.descendants((node, pos) => {\n if (\n node.type.name === \"imageBlock\" &&\n node.attrs.loading\n ) {\n foundPos = pos\n return false\n }\n })\n\n if (foundPos !== -1) {\n const transaction = view.state.tr.replaceWith(\n foundPos,\n foundPos + 1,\n node\n )\n view.dispatch(transaction)\n }\n } catch (error) {\n console.error(\"Failed to upload image:\", error)\n // Remove placeholder on error\n const currentState = view.state\n let foundPos = -1\n currentState.doc.descendants((node, pos) => {\n if (\n node.type.name === \"imageBlock\" &&\n node.attrs.loading\n ) {\n foundPos = pos\n return false\n }\n })\n\n if (foundPos !== -1) {\n const transaction = view.state.tr.delete(\n foundPos,\n foundPos + 1\n )\n view.dispatch(transaction)\n }\n }\n }\n })\n\n return true\n },\n },\n },\n }),\n ]\n },\n})\n\nexport default ImageBlock\n","import { ReactRenderer } from \"@tiptap/react\"\nimport Suggestion from \"@tiptap/suggestion\"\nimport { Extension } from \"@tiptap/core\"\nimport type { RefObject, ReactNode } from \"react\"\nimport { EditorCommandOut } from \"../components/editor-command\"\n\nexport const Command = Extension.create({\n name: \"slash-command\",\n addOptions() {\n return {\n suggestion: {\n char: \"/\",\n command: (ctx: any) => {\n ctx.props.command({ editor: ctx.editor, range: ctx.range })\n },\n } as any,\n }\n },\n addProseMirrorPlugins() {\n const base: any = this.options.suggestion ?? {}\n return [\n Suggestion({\n editor: this.editor,\n char: base.char ?? \"/\",\n items: base.items ?? (() => [\"/\"] as any),\n command: (ctx: any) => {\n if (typeof ctx?.props?.command === \"function\") {\n ctx.props.command({ editor: ctx.editor, range: ctx.range })\n }\n },\n ...base,\n }),\n ]\n },\n})\n\nexport const renderItems = (elementRef?: RefObject<Element> | null) => {\n let component: ReactRenderer | null = null\n let container: HTMLElement | null = null\n\n const destroy = () => {\n component?.destroy()\n component = null\n if (container) {\n container.remove()\n container = null\n }\n }\n\n const updatePosition = (clientRect?: DOMRect | null) => {\n if (!container || !clientRect) return\n const top = Math.round(clientRect.bottom + 8)\n const left = Math.round(clientRect.left)\n container.style.top = `${top}px`\n container.style.left = `${left}px`\n }\n\n return {\n onStart: (props: {\n editor: any\n clientRect: (() => DOMRect | null) | null\n query?: string\n range?: any\n }) => {\n const { selection } = props.editor.state\n const parentNode = selection.$from.node(selection.$from.depth)\n const blockType = parentNode.type.name\n if (blockType === \"codeBlock\") return false\n\n component = new ReactRenderer(EditorCommandOut, {\n props: {\n query: (props as any).query ?? \"\",\n range: (props as any).range,\n },\n editor: props.editor,\n })\n\n container = document.createElement(\"div\")\n container.style.position = \"fixed\"\n container.style.zIndex = \"9999\"\n container.style.minWidth = \"240px\"\n ;(elementRef?.current ?? document.body).appendChild(container)\n container.appendChild(component.element)\n\n const rect =\n typeof props.clientRect === \"function\" ? props.clientRect() : null\n if (rect) updatePosition(rect)\n },\n onUpdate: (props: {\n editor: any\n clientRect: (() => DOMRect | null) | null\n query?: string\n range?: any\n }) => {\n component?.updateProps({\n query: (props as any).query ?? \"\",\n range: (props as any).range,\n })\n const rect =\n typeof props.clientRect === \"function\" ? props.clientRect() : null\n if (rect) updatePosition(rect)\n },\n onKeyDown: ({ event }: { event: KeyboardEvent }) => {\n if (event.key === \"Escape\") {\n destroy()\n return true\n }\n return false\n },\n onExit: () => {\n destroy()\n },\n }\n}\n\nexport interface SuggestionItem {\n title: string\n description: string\n icon: ReactNode\n searchTerms?: string[]\n command?: (props: {\n editor: any\n range: { from: number; to: number }\n }) => void\n}\n\nexport const createSuggestionItems = (items: SuggestionItem[]) => items\n\nexport const handleCommandNavigation = (event: KeyboardEvent) => {\n if ([\"ArrowUp\", \"ArrowDown\", \"Enter\"].includes(event.key)) {\n const slashCommand = document.querySelector(\"#slash-command\")\n if (slashCommand) return true\n }\n}\n"],"mappings":"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","names":["useEffect","jsx","jsxs","useCallback","useState","useEffect","jsx","jsxs","jsx","jsxs","useCallback","useRef","useState","jsx","jsxs","useCallback","useRef","jsx","jsxs","useCurrentEditor","jsx","forwardRef","useCurrentEditor","jsx","forwardRef","jsx","forwardRef","forwardRef","useCurrentEditor","jsx","forwardRef","useCurrentEditor","mergeAttributes","Plugin","node","Command"]}
@@ -86,25 +86,51 @@ var init_ImageBlockWidth = __esm({
86
86
  }
87
87
  });
88
88
 
89
+ // src/react/menus/bubble-menu-extensions.tsx
90
+ import { createContext, useContext, useMemo } from "react";
91
+ import { jsx as jsx7 } from "react/jsx-runtime";
92
+ var BubbleMenuExtensionsContext, BubbleMenuExtensionsProvider, useBubbleMenuExtensions;
93
+ var init_bubble_menu_extensions = __esm({
94
+ "src/react/menus/bubble-menu-extensions.tsx"() {
95
+ "use strict";
96
+ BubbleMenuExtensionsContext = createContext([]);
97
+ BubbleMenuExtensionsProvider = ({
98
+ extensions,
99
+ children
100
+ }) => {
101
+ return /* @__PURE__ */ jsx7(BubbleMenuExtensionsContext.Provider, { value: extensions ?? [], children });
102
+ };
103
+ useBubbleMenuExtensions = (target, editor) => {
104
+ const extensions = useContext(BubbleMenuExtensionsContext);
105
+ return useMemo(() => {
106
+ if (!editor) return [];
107
+ return extensions.filter((extension) => extension.target === target).filter((extension) => extension.when ? extension.when({ editor }) : true).sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
108
+ }, [editor, extensions, target]);
109
+ };
110
+ }
111
+ });
112
+
89
113
  // src/react/menus/ImageBlock/ImageBlockMenu.tsx
90
114
  import { BubbleMenu } from "@tiptap/react/menus";
91
- import { useCallback as useCallback2, useRef, useState as useState2, useEffect as useEffect3 } from "react";
115
+ import { Fragment, useCallback as useCallback2, useRef, useState as useState2, useEffect as useEffect3 } from "react";
92
116
  import {
93
117
  IconAlignLeft,
94
118
  IconAlignCenter,
95
119
  IconAlignRight,
96
120
  IconTrash
97
121
  } from "@tabler/icons-react";
98
- import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
122
+ import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
99
123
  var ImageBlockMenu;
100
124
  var init_ImageBlockMenu = __esm({
101
125
  "src/react/menus/ImageBlock/ImageBlockMenu.tsx"() {
102
126
  "use strict";
103
127
  init_ImageBlockWidth();
128
+ init_bubble_menu_extensions();
104
129
  ImageBlockMenu = ({ editor, appendTo }) => {
105
130
  const menuRef = useRef(null);
106
131
  const [align, setAlign] = useState2("center");
107
132
  const [width, setWidth] = useState2(100);
133
+ const extensions = useBubbleMenuExtensions("imageBlock", editor);
108
134
  useEffect3(() => {
109
135
  if (!editor) return;
110
136
  const update = () => {
@@ -170,14 +196,14 @@ var init_ImageBlockMenu = __esm({
170
196
  const onRemoveImage = useCallback2(() => {
171
197
  editor.chain().focus(void 0, { scrollIntoView: false }).deleteSelection().run();
172
198
  }, [editor]);
173
- return /* @__PURE__ */ jsx7(
199
+ return /* @__PURE__ */ jsx8(
174
200
  BubbleMenu,
175
201
  {
176
202
  editor,
177
203
  shouldShow,
178
204
  updateDelay: 0,
179
205
  children: /* @__PURE__ */ jsxs3("div", { className: "bubble-menu", ref: menuRef, children: [
180
- /* @__PURE__ */ jsx7(
206
+ /* @__PURE__ */ jsx8(
181
207
  "button",
182
208
  {
183
209
  type: "button",
@@ -185,10 +211,10 @@ var init_ImageBlockMenu = __esm({
185
211
  title: "Align left",
186
212
  onMouseDown: (e) => e.preventDefault(),
187
213
  onClick: onAlignImageLeft,
188
- children: /* @__PURE__ */ jsx7(IconAlignLeft, { size: 16 })
214
+ children: /* @__PURE__ */ jsx8(IconAlignLeft, { size: 16 })
189
215
  }
190
216
  ),
191
- /* @__PURE__ */ jsx7(
217
+ /* @__PURE__ */ jsx8(
192
218
  "button",
193
219
  {
194
220
  type: "button",
@@ -196,10 +222,10 @@ var init_ImageBlockMenu = __esm({
196
222
  title: "Align center",
197
223
  onMouseDown: (e) => e.preventDefault(),
198
224
  onClick: onAlignImageCenter,
199
- children: /* @__PURE__ */ jsx7(IconAlignCenter, { size: 16 })
225
+ children: /* @__PURE__ */ jsx8(IconAlignCenter, { size: 16 })
200
226
  }
201
227
  ),
202
- /* @__PURE__ */ jsx7(
228
+ /* @__PURE__ */ jsx8(
203
229
  "button",
204
230
  {
205
231
  type: "button",
@@ -207,25 +233,25 @@ var init_ImageBlockMenu = __esm({
207
233
  title: "Align right",
208
234
  onMouseDown: (e) => e.preventDefault(),
209
235
  onClick: onAlignImageRight,
210
- children: /* @__PURE__ */ jsx7(IconAlignRight, { size: 16 })
236
+ children: /* @__PURE__ */ jsx8(IconAlignRight, { size: 16 })
211
237
  }
212
238
  ),
213
- /* @__PURE__ */ jsx7(
239
+ /* @__PURE__ */ jsx8(
214
240
  "div",
215
241
  {
216
242
  className: "nph-link-popover__divider",
217
243
  style: { margin: "0 4px" }
218
244
  }
219
245
  ),
220
- /* @__PURE__ */ jsx7(ImageBlockWidth, { onChange: onWidthChange, value: width }),
221
- /* @__PURE__ */ jsx7(
246
+ /* @__PURE__ */ jsx8(ImageBlockWidth, { onChange: onWidthChange, value: width }),
247
+ /* @__PURE__ */ jsx8(
222
248
  "div",
223
249
  {
224
250
  className: "nph-link-popover__divider",
225
251
  style: { margin: "0 4px" }
226
252
  }
227
253
  ),
228
- /* @__PURE__ */ jsx7(
254
+ /* @__PURE__ */ jsx8(
229
255
  "button",
230
256
  {
231
257
  type: "button",
@@ -233,9 +259,10 @@ var init_ImageBlockMenu = __esm({
233
259
  title: "Remove image",
234
260
  onMouseDown: (e) => e.preventDefault(),
235
261
  onClick: onRemoveImage,
236
- children: /* @__PURE__ */ jsx7(IconTrash, { size: 16 })
262
+ children: /* @__PURE__ */ jsx8(IconTrash, { size: 16 })
237
263
  }
238
- )
264
+ ),
265
+ extensions.map((extension) => /* @__PURE__ */ jsx8(Fragment, { children: extension.render({ editor }) }, extension.id))
239
266
  ] })
240
267
  }
241
268
  );
@@ -245,18 +272,18 @@ var init_ImageBlockMenu = __esm({
245
272
 
246
273
  // src/react/menus/ImageBlock/ImageBlockLoading.tsx
247
274
  import { IconLoader2 } from "@tabler/icons-react";
248
- import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
275
+ import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
249
276
  var ImageBlockLoading;
250
277
  var init_ImageBlockLoading = __esm({
251
278
  "src/react/menus/ImageBlock/ImageBlockLoading.tsx"() {
252
279
  "use strict";
253
280
  ImageBlockLoading = () => {
254
281
  return /* @__PURE__ */ jsxs4("div", { className: "nph-image-block-loading", children: [
255
- /* @__PURE__ */ jsx8("div", { className: "nph-image-block-loading__overlay", children: /* @__PURE__ */ jsxs4("div", { className: "nph-image-block-loading__content", children: [
256
- /* @__PURE__ */ jsx8(IconLoader2, { size: 24, className: "nph-image-block-loading__spinner" }),
257
- /* @__PURE__ */ jsx8("p", { className: "nph-image-block-loading__text", children: "Uploading image..." })
282
+ /* @__PURE__ */ jsx9("div", { className: "nph-image-block-loading__overlay", children: /* @__PURE__ */ jsxs4("div", { className: "nph-image-block-loading__content", children: [
283
+ /* @__PURE__ */ jsx9(IconLoader2, { size: 24, className: "nph-image-block-loading__spinner" }),
284
+ /* @__PURE__ */ jsx9("p", { className: "nph-image-block-loading__text", children: "Uploading image..." })
258
285
  ] }) }),
259
- /* @__PURE__ */ jsx8("div", { className: "nph-image-block-loading__placeholder" })
286
+ /* @__PURE__ */ jsx9("div", { className: "nph-image-block-loading__placeholder" })
260
287
  ] });
261
288
  };
262
289
  }
@@ -265,7 +292,7 @@ var init_ImageBlockLoading = __esm({
265
292
  // src/react/menus/ImageBlock/ImageUploader.tsx
266
293
  import { IconPhoto, IconUpload } from "@tabler/icons-react";
267
294
  import { useCallback as useCallback3, useRef as useRef2, useState as useState3 } from "react";
268
- import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
295
+ import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
269
296
  var ImageUploader;
270
297
  var init_ImageUploader = __esm({
271
298
  "src/react/menus/ImageBlock/ImageUploader.tsx"() {
@@ -332,7 +359,7 @@ var init_ImageUploader = __esm({
332
359
  setDraggedInside(false);
333
360
  }, []);
334
361
  if (loading) {
335
- return /* @__PURE__ */ jsx9(ImageBlockLoading, {});
362
+ return /* @__PURE__ */ jsx10(ImageBlockLoading, {});
336
363
  }
337
364
  return /* @__PURE__ */ jsxs5(
338
365
  "div",
@@ -343,10 +370,10 @@ var init_ImageUploader = __esm({
343
370
  onDragLeave,
344
371
  contentEditable: false,
345
372
  children: [
346
- /* @__PURE__ */ jsx9(IconPhoto, { size: 48, className: "nph-image-uploader__icon" }),
373
+ /* @__PURE__ */ jsx10(IconPhoto, { size: 48, className: "nph-image-uploader__icon" }),
347
374
  /* @__PURE__ */ jsxs5("div", { className: "nph-image-uploader__content", children: [
348
- /* @__PURE__ */ jsx9("div", { className: "nph-image-uploader__text", children: draggedInside ? "Drop image here" : "Drag and drop or" }),
349
- /* @__PURE__ */ jsx9("div", { children: /* @__PURE__ */ jsxs5(
375
+ /* @__PURE__ */ jsx10("div", { className: "nph-image-uploader__text", children: draggedInside ? "Drop image here" : "Drag and drop or" }),
376
+ /* @__PURE__ */ jsx10("div", { children: /* @__PURE__ */ jsxs5(
350
377
  "button",
351
378
  {
352
379
  type: "button",
@@ -354,13 +381,13 @@ var init_ImageUploader = __esm({
354
381
  onClick: handleUploadClick,
355
382
  className: "nph-btn nph-btn-ghost nph-btn-sm nph-image-uploader__button",
356
383
  children: [
357
- /* @__PURE__ */ jsx9(IconUpload, { size: 16 }),
384
+ /* @__PURE__ */ jsx10(IconUpload, { size: 16 }),
358
385
  "Upload an image"
359
386
  ]
360
387
  }
361
388
  ) })
362
389
  ] }),
363
- /* @__PURE__ */ jsx9(
390
+ /* @__PURE__ */ jsx10(
364
391
  "input",
365
392
  {
366
393
  ref: fileInputRef,
@@ -385,14 +412,14 @@ __export(ImageBlockView_exports, {
385
412
  });
386
413
  import { NodeViewWrapper } from "@tiptap/react";
387
414
  import { useCallback as useCallback4, useRef as useRef3 } from "react";
388
- import { IconLoader2 as IconLoader22 } from "@tabler/icons-react";
389
- import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
415
+ import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
390
416
  var ImageBlockView, ImageBlockView_default;
391
417
  var init_ImageBlockView = __esm({
392
418
  "src/react/menus/ImageBlock/ImageBlockView.tsx"() {
393
419
  "use strict";
394
420
  init_ImageBlockMenu();
395
421
  init_ImageUploader();
422
+ init_ImageBlockLoading();
396
423
  ImageBlockView = (props) => {
397
424
  const { editor, getPos, node, updateAttributes } = props;
398
425
  const imageWrapperRef = useRef3(null);
@@ -420,32 +447,13 @@ var init_ImageBlockView = __esm({
420
447
  }
421
448
  };
422
449
  if (!src || src === "") {
423
- return /* @__PURE__ */ jsx10(NodeViewWrapper, { children: /* @__PURE__ */ jsx10("div", { style: getWrapperStyle(), children: /* @__PURE__ */ jsx10("div", { ref: imageWrapperRef, children: /* @__PURE__ */ jsx10(ImageUploader, { onUpload: handleUpload, editor }) }) }) });
450
+ return /* @__PURE__ */ jsx11(NodeViewWrapper, { children: /* @__PURE__ */ jsx11("div", { style: getWrapperStyle(), children: /* @__PURE__ */ jsx11("div", { ref: imageWrapperRef, children: /* @__PURE__ */ jsx11(ImageUploader, { onUpload: handleUpload, editor }) }) }) });
424
451
  }
425
452
  if (loading) {
426
- return /* @__PURE__ */ jsx10(NodeViewWrapper, { children: /* @__PURE__ */ jsx10("div", { style: getWrapperStyle(), children: /* @__PURE__ */ jsxs6(
427
- "div",
428
- {
429
- ref: imageWrapperRef,
430
- className: "nph-image-block-loading",
431
- children: [
432
- /* @__PURE__ */ jsx10("div", { className: "nph-image-block-loading__overlay", children: /* @__PURE__ */ jsxs6("div", { className: "nph-image-block-loading__content", children: [
433
- /* @__PURE__ */ jsx10(
434
- IconLoader22,
435
- {
436
- size: 24,
437
- className: "nph-image-block-loading__spinner"
438
- }
439
- ),
440
- /* @__PURE__ */ jsx10("p", { className: "nph-image-block-loading__text", children: "Uploading image..." })
441
- ] }) }),
442
- /* @__PURE__ */ jsx10("div", { className: "nph-image-block-loading__placeholder" })
443
- ]
444
- }
445
- ) }) });
453
+ return /* @__PURE__ */ jsx11(NodeViewWrapper, { children: /* @__PURE__ */ jsx11("div", { style: getWrapperStyle(), children: /* @__PURE__ */ jsx11("div", { ref: imageWrapperRef, children: /* @__PURE__ */ jsx11(ImageBlockLoading, {}) }) }) });
446
454
  }
447
455
  return /* @__PURE__ */ jsxs6(NodeViewWrapper, { children: [
448
- /* @__PURE__ */ jsx10("div", { style: getWrapperStyle(), children: /* @__PURE__ */ jsx10("div", { contentEditable: false, ref: imageWrapperRef, style: { position: "relative" }, children: /* @__PURE__ */ jsx10(
456
+ /* @__PURE__ */ jsx11("div", { style: getWrapperStyle(), children: /* @__PURE__ */ jsx11("div", { contentEditable: false, ref: imageWrapperRef, style: { position: "relative" }, children: /* @__PURE__ */ jsx11(
449
457
  "img",
450
458
  {
451
459
  src,
@@ -454,7 +462,7 @@ var init_ImageBlockView = __esm({
454
462
  className: "nph-image-block"
455
463
  }
456
464
  ) }) }),
457
- /* @__PURE__ */ jsx10(ImageBlockMenu, { editor, appendTo: imageWrapperRef })
465
+ /* @__PURE__ */ jsx11(ImageBlockMenu, { editor, appendTo: imageWrapperRef })
458
466
  ] });
459
467
  };
460
468
  ImageBlockView_default = ImageBlockView;
@@ -619,6 +627,7 @@ import { useCurrentEditor as useCurrentEditor3 } from "@tiptap/react";
619
627
  import { useAtomValue } from "jotai";
620
628
  import { jsx as jsx5 } from "react/jsx-runtime";
621
629
  var CommandItemAny = CommandItem;
630
+ var CommandEmptyAny = CommandEmpty;
622
631
  var EditorCommandItem = forwardRef4(({ children, onCommand, ...rest }, ref) => {
623
632
  const { editor } = useCurrentEditor3();
624
633
  const range = useAtomValue(rangeAtom);
@@ -634,6 +643,7 @@ var EditorCommandItem = forwardRef4(({ children, onCommand, ...rest }, ref) => {
634
643
  );
635
644
  });
636
645
  EditorCommandItem.displayName = "EditorCommandItem";
646
+ var EditorCommandEmpty = CommandEmptyAny;
637
647
 
638
648
  // src/headless/extensions/index.ts
639
649
  import { StarterKit } from "@tiptap/starter-kit";
@@ -1043,8 +1053,12 @@ export {
1043
1053
  EditorCommand,
1044
1054
  EditorCommandList,
1045
1055
  EditorCommandItem,
1056
+ EditorCommandEmpty,
1046
1057
  CodeBlock,
1047
1058
  Link,
1059
+ BubbleMenuExtensionsProvider,
1060
+ useBubbleMenuExtensions,
1061
+ init_bubble_menu_extensions,
1048
1062
  ImageBlock,
1049
1063
  StarterKit,
1050
1064
  Placeholder,
@@ -1054,4 +1068,4 @@ export {
1054
1068
  handleCommandNavigation,
1055
1069
  useCurrentEditor4 as useCurrentEditor
1056
1070
  };
1057
- //# sourceMappingURL=chunk-7L2HLBM2.js.map
1071
+ //# sourceMappingURL=chunk-TPFBGHW3.js.map