neuphlo-editor 2.1.1 → 2.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.
@@ -19,7 +19,7 @@ import {
19
19
  queryAtom,
20
20
  setDragHandleCallbacks,
21
21
  store_exports
22
- } from "../chunk-MSNTVITF.js";
22
+ } from "../chunk-VPI26I4P.js";
23
23
 
24
24
  // src/headless/extensions/extension-kit.ts
25
25
  import Collaboration from "@tiptap/extension-collaboration";
@@ -93,10 +93,11 @@ var VideoBlock = Node.create({
93
93
  };
94
94
  },
95
95
  addNodeView() {
96
+ console.log("[VideoBlock] addNodeView called, options:", Object.keys(this.options), "hasNodeView:", !!this.options.nodeView);
96
97
  if (this.options.nodeView) {
97
98
  return ReactNodeViewRenderer(this.options.nodeView);
98
99
  }
99
- return null;
100
+ return void 0;
100
101
  }
101
102
  });
102
103
 
@@ -1267,7 +1268,8 @@ function ImageMenu({
1267
1268
 
1268
1269
  // src/react/menus/ImageBlock/ImageBlockView.tsx
1269
1270
  import { NodeViewWrapper } from "@tiptap/react";
1270
- import { useCallback as useCallback7, useRef as useRef7 } from "react";
1271
+ import { useCallback as useCallback7, useRef as useRef7, useState as useState8 } from "react";
1272
+ import { IconPhotoOff } from "@tabler/icons-react";
1271
1273
 
1272
1274
  // src/react/menus/ImageBlock/ImageBlockMenu.tsx
1273
1275
  import { NodeSelection as NodeSelection3 } from "@tiptap/pm/state";
@@ -1351,6 +1353,7 @@ var ImageBlockMenu = ({ editor, getPos, appendTo }) => {
1351
1353
  const { state } = ctx.editor;
1352
1354
  const { selection } = state;
1353
1355
  if (!ctx.editor.isEditable) return { isVisible: false, align: "center", width: 100 };
1356
+ if (!ctx.editor.isFocused) return { isVisible: false, align: "center", width: 100 };
1354
1357
  const isNodeSel = selection instanceof NodeSelection3;
1355
1358
  const isThisNode = isNodeSel && selection.from === getPos();
1356
1359
  const visible = isThisNode;
@@ -1544,6 +1547,31 @@ var ImageUploader = ({ onUpload, editor }) => {
1544
1547
  if (loading) {
1545
1548
  return /* @__PURE__ */ jsx9(ImageBlockLoading, {});
1546
1549
  }
1550
+ if (browseAssets) {
1551
+ return /* @__PURE__ */ jsx9(
1552
+ "div",
1553
+ {
1554
+ className: "nph-image-uploader nph-image-uploader--browse-only",
1555
+ contentEditable: false,
1556
+ onClick: () => browseAssets(onUpload),
1557
+ role: "button",
1558
+ tabIndex: 0,
1559
+ onKeyDown: (e) => {
1560
+ if (e.key === "Enter" || e.key === " ") {
1561
+ e.preventDefault();
1562
+ browseAssets(onUpload);
1563
+ }
1564
+ },
1565
+ children: /* @__PURE__ */ jsxs9("div", { className: "nph-image-uploader__browse-cta", children: [
1566
+ /* @__PURE__ */ jsx9("div", { className: "nph-image-uploader__browse-icon-wrapper", children: /* @__PURE__ */ jsx9(IconPhoto2, { size: 28 }) }),
1567
+ /* @__PURE__ */ jsxs9("div", { className: "nph-image-uploader__browse-text", children: [
1568
+ /* @__PURE__ */ jsx9("span", { className: "nph-image-uploader__browse-title", children: "Choose from assets" }),
1569
+ /* @__PURE__ */ jsx9("span", { className: "nph-image-uploader__browse-subtitle", children: "Select an image from your library" })
1570
+ ] })
1571
+ ] })
1572
+ }
1573
+ );
1574
+ }
1547
1575
  return /* @__PURE__ */ jsxs9(
1548
1576
  "div",
1549
1577
  {
@@ -1556,34 +1584,19 @@ var ImageUploader = ({ onUpload, editor }) => {
1556
1584
  /* @__PURE__ */ jsx9(IconPhoto2, { size: 48, className: "nph-image-uploader__icon" }),
1557
1585
  /* @__PURE__ */ jsxs9("div", { className: "nph-image-uploader__content", children: [
1558
1586
  /* @__PURE__ */ jsx9("div", { className: "nph-image-uploader__text", children: draggedInside ? "Drop image here" : "Drag and drop or" }),
1559
- /* @__PURE__ */ jsxs9("div", { className: "nph-image-uploader__actions", children: [
1560
- /* @__PURE__ */ jsxs9(
1561
- "button",
1562
- {
1563
- type: "button",
1564
- disabled: draggedInside,
1565
- onClick: handleUploadClick,
1566
- className: "nph-btn nph-btn-ghost nph-btn-sm nph-image-uploader__button",
1567
- children: [
1568
- /* @__PURE__ */ jsx9(IconUpload2, { size: 16 }),
1569
- "Upload an image"
1570
- ]
1571
- }
1572
- ),
1573
- browseAssets && /* @__PURE__ */ jsxs9(
1574
- "button",
1575
- {
1576
- type: "button",
1577
- disabled: draggedInside,
1578
- onClick: () => browseAssets(onUpload),
1579
- className: "nph-btn nph-btn-ghost nph-btn-sm nph-image-uploader__button",
1580
- children: [
1581
- /* @__PURE__ */ jsx9(IconPhoto2, { size: 16 }),
1582
- "Browse assets"
1583
- ]
1584
- }
1585
- )
1586
- ] })
1587
+ /* @__PURE__ */ jsx9("div", { className: "nph-image-uploader__actions", children: /* @__PURE__ */ jsxs9(
1588
+ "button",
1589
+ {
1590
+ type: "button",
1591
+ disabled: draggedInside,
1592
+ onClick: handleUploadClick,
1593
+ className: "nph-btn nph-btn-ghost nph-btn-sm nph-image-uploader__button",
1594
+ children: [
1595
+ /* @__PURE__ */ jsx9(IconUpload2, { size: 16 }),
1596
+ "Upload an image"
1597
+ ]
1598
+ }
1599
+ ) })
1587
1600
  ] }),
1588
1601
  /* @__PURE__ */ jsx9(
1589
1602
  "input",
@@ -1671,6 +1684,7 @@ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1671
1684
  var ImageBlockView = (props) => {
1672
1685
  const { editor, getPos, node, updateAttributes } = props;
1673
1686
  const imageWrapperRef = useRef7(null);
1687
+ const [imageError, setImageError] = useState8(false);
1674
1688
  const { src, width, align, alt, loading } = node.attrs;
1675
1689
  const handleUpload = useCallback7(
1676
1690
  (url) => {
@@ -1710,6 +1724,15 @@ var ImageBlockView = (props) => {
1710
1724
  if (loading) {
1711
1725
  return /* @__PURE__ */ jsx11(NodeViewWrapper, { style: getWrapperStyle(), children: /* @__PURE__ */ jsx11("div", { ref: imageWrapperRef, children: /* @__PURE__ */ jsx11(ImageBlockLoading, {}) }) });
1712
1726
  }
1727
+ if (imageError) {
1728
+ return /* @__PURE__ */ jsx11(NodeViewWrapper, { style: getWrapperStyle(), children: /* @__PURE__ */ jsxs11("div", { contentEditable: false, ref: imageWrapperRef, style: getContentStyle(), children: [
1729
+ /* @__PURE__ */ jsxs11("div", { className: "nph-image-block-error", onClick, children: [
1730
+ /* @__PURE__ */ jsx11(IconPhotoOff, { size: 32 }),
1731
+ /* @__PURE__ */ jsx11("span", { children: "Image could not be loaded" })
1732
+ ] }),
1733
+ editor.isEditable && /* @__PURE__ */ jsx11(ImageBlockMenu, { editor, getPos, appendTo: imageWrapperRef })
1734
+ ] }) });
1735
+ }
1713
1736
  return /* @__PURE__ */ jsx11(NodeViewWrapper, { style: getWrapperStyle(), children: /* @__PURE__ */ jsxs11("div", { contentEditable: false, ref: imageWrapperRef, style: getContentStyle(), children: [
1714
1737
  editor.isEditable ? /* @__PURE__ */ jsx11(ImageResizeHandle, { onResize: handleResize, currentWidth: width, children: /* @__PURE__ */ jsx11(
1715
1738
  "img",
@@ -1717,6 +1740,7 @@ var ImageBlockView = (props) => {
1717
1740
  src,
1718
1741
  alt: alt || "",
1719
1742
  onClick,
1743
+ onError: () => setImageError(true),
1720
1744
  className: "nph-image-block"
1721
1745
  }
1722
1746
  ) }) : /* @__PURE__ */ jsx11(
@@ -1724,6 +1748,7 @@ var ImageBlockView = (props) => {
1724
1748
  {
1725
1749
  src,
1726
1750
  alt: alt || "",
1751
+ onError: () => setImageError(true),
1727
1752
  className: "nph-image-block"
1728
1753
  }
1729
1754
  ),
@@ -1734,7 +1759,7 @@ var ImageBlockView = (props) => {
1734
1759
  // src/react/menus/VideoBlock/VideoBlockView.tsx
1735
1760
  import { NodeSelection as NodeSelection5 } from "@tiptap/pm/state";
1736
1761
  import { NodeViewWrapper as NodeViewWrapper2, useEditorState as useEditorState4 } from "@tiptap/react";
1737
- import { useCallback as useCallback9, useRef as useRef9, useState as useState8 } from "react";
1762
+ import { useCallback as useCallback9, useRef as useRef9, useState as useState9 } from "react";
1738
1763
 
1739
1764
  // src/react/menus/VideoBlock/VideoBlockMenu.tsx
1740
1765
  import { NodeSelection as NodeSelection4 } from "@tiptap/pm/state";
@@ -1866,26 +1891,34 @@ var VideoBlockMenu = ({ editor, getPos }) => {
1866
1891
  };
1867
1892
 
1868
1893
  // src/react/menus/VideoBlock/VideoBlockView.tsx
1869
- import { IconVideo as IconVideo2 } from "@tabler/icons-react";
1894
+ import { IconVideo as IconVideo2, IconVideoOff } from "@tabler/icons-react";
1870
1895
  import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1871
1896
  function toEmbedUrl(url) {
1897
+ if (url.includes("/embed/") || url.includes("player.vimeo.com") || url.includes("loom.com/embed")) {
1898
+ return url;
1899
+ }
1872
1900
  const ytMatch = url.match(
1873
- /(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)/
1901
+ /(?:youtube\.com\/(?:watch\?v=|shorts\/)|youtu\.be\/)([\w-]+)/
1874
1902
  );
1875
1903
  if (ytMatch) {
1876
1904
  return `https://www.youtube.com/embed/${ytMatch[1]}`;
1877
1905
  }
1878
- const vimeoMatch = url.match(/vimeo\.com\/(\d+)/);
1906
+ const vimeoMatch = url.match(/vimeo\.com\/(?:channels\/[\w-]+\/)?(\d+)/);
1879
1907
  if (vimeoMatch) {
1880
1908
  return `https://player.vimeo.com/video/${vimeoMatch[1]}`;
1881
1909
  }
1910
+ const loomMatch = url.match(/loom\.com\/share\/([\w-]+)/);
1911
+ if (loomMatch) {
1912
+ return `https://www.loom.com/embed/${loomMatch[1]}`;
1913
+ }
1882
1914
  return url;
1883
1915
  }
1884
1916
  var VideoBlockView = (props) => {
1885
1917
  const { editor, getPos, node, updateAttributes } = props;
1886
1918
  const wrapperRef = useRef9(null);
1887
1919
  const { src, width, align } = node.attrs;
1888
- const [inputUrl, setInputUrl] = useState8("");
1920
+ const [inputUrl, setInputUrl] = useState9("");
1921
+ const [videoError, setVideoError] = useState9(false);
1889
1922
  const isSelected = useEditorState4({
1890
1923
  editor,
1891
1924
  selector: (ctx) => {
@@ -1913,8 +1946,8 @@ var VideoBlockView = (props) => {
1913
1946
  }, [getPos, editor.commands]);
1914
1947
  const getWrapperStyle = () => {
1915
1948
  const baseStyle = {
1916
- width: "fit-content",
1917
- maxWidth: width || "100%"
1949
+ width: width || "100%",
1950
+ maxWidth: "100%"
1918
1951
  };
1919
1952
  if (align === "left") {
1920
1953
  return { ...baseStyle, marginLeft: 0, marginRight: "auto" };
@@ -1926,18 +1959,23 @@ var VideoBlockView = (props) => {
1926
1959
  };
1927
1960
  if (!src || src === "") {
1928
1961
  if (!editor.isEditable) return /* @__PURE__ */ jsx13(NodeViewWrapper2, {});
1929
- return /* @__PURE__ */ jsx13(NodeViewWrapper2, { style: getWrapperStyle(), children: /* @__PURE__ */ jsx13("div", { ref: wrapperRef, children: /* @__PURE__ */ jsxs13("div", { className: "nph-video-input", children: [
1930
- /* @__PURE__ */ jsx13("div", { className: "nph-video-input__icon", children: /* @__PURE__ */ jsx13(IconVideo2, { size: 24 }) }),
1931
- /* @__PURE__ */ jsxs13("div", { className: "nph-video-input__content", children: [
1962
+ return /* @__PURE__ */ jsx13(NodeViewWrapper2, { style: getWrapperStyle(), children: /* @__PURE__ */ jsx13("div", { ref: wrapperRef, children: /* @__PURE__ */ jsxs13("div", { className: "nph-video-placeholder", onClick, children: [
1963
+ /* @__PURE__ */ jsx13("div", { className: "nph-video-placeholder__icon", children: /* @__PURE__ */ jsx13(IconVideo2, { size: 28, stroke: 1.5 }) }),
1964
+ /* @__PURE__ */ jsxs13("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: "2px" }, children: [
1965
+ /* @__PURE__ */ jsx13("div", { style: { fontSize: "14px", fontWeight: 600, lineHeight: "20px" }, children: "Add a video" }),
1966
+ /* @__PURE__ */ jsx13("div", { style: { fontSize: "13px", opacity: 0.5, lineHeight: "18px" }, children: "Paste a URL to embed" })
1967
+ ] }),
1968
+ /* @__PURE__ */ jsxs13("div", { className: "nph-video-placeholder__input", children: [
1932
1969
  /* @__PURE__ */ jsx13(
1933
1970
  "input",
1934
1971
  {
1935
1972
  type: "text",
1936
1973
  className: "nph-video-input__field",
1937
- placeholder: "Paste a YouTube, Vimeo, or video URL...",
1974
+ placeholder: "YouTube, Vimeo, or Loom URL...",
1938
1975
  value: inputUrl,
1939
1976
  onChange: (e) => setInputUrl(e.target.value),
1940
- onKeyDown: handleKeyDown
1977
+ onKeyDown: handleKeyDown,
1978
+ onClick: (e) => e.stopPropagation()
1941
1979
  }
1942
1980
  ),
1943
1981
  /* @__PURE__ */ jsx13(
@@ -1945,7 +1983,10 @@ var VideoBlockView = (props) => {
1945
1983
  {
1946
1984
  type: "button",
1947
1985
  className: "nph-video-input__button",
1948
- onClick: handleEmbed,
1986
+ onClick: (e) => {
1987
+ e.stopPropagation();
1988
+ handleEmbed();
1989
+ },
1949
1990
  disabled: !inputUrl.trim(),
1950
1991
  children: "Embed"
1951
1992
  }
@@ -1953,6 +1994,16 @@ var VideoBlockView = (props) => {
1953
1994
  ] })
1954
1995
  ] }) }) });
1955
1996
  }
1997
+ if (videoError) {
1998
+ return /* @__PURE__ */ jsx13(NodeViewWrapper2, { style: getWrapperStyle(), children: /* @__PURE__ */ jsxs13("div", { contentEditable: false, ref: wrapperRef, style: { position: "relative" }, children: [
1999
+ /* @__PURE__ */ jsxs13("div", { className: "nph-video-block-error", onClick, children: [
2000
+ /* @__PURE__ */ jsx13(IconVideoOff, { size: 32 }),
2001
+ /* @__PURE__ */ jsx13("span", { children: "Video could not be loaded" }),
2002
+ /* @__PURE__ */ jsx13("span", { className: "nph-video-block-error__url", children: src })
2003
+ ] }),
2004
+ editor.isEditable && /* @__PURE__ */ jsx13(VideoBlockMenu, { editor, getPos })
2005
+ ] }) });
2006
+ }
1956
2007
  return /* @__PURE__ */ jsx13(NodeViewWrapper2, { style: getWrapperStyle(), children: /* @__PURE__ */ jsxs13(
1957
2008
  "div",
1958
2009
  {
@@ -1967,7 +2018,8 @@ var VideoBlockView = (props) => {
1967
2018
  src,
1968
2019
  className: "nph-video-block__iframe",
1969
2020
  allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
1970
- allowFullScreen: true
2021
+ allowFullScreen: true,
2022
+ onError: () => setVideoError(true)
1971
2023
  }
1972
2024
  ),
1973
2025
  !isSelected && /* @__PURE__ */ jsx13(
@@ -2172,7 +2224,7 @@ import {
2172
2224
  IconRowInsertTop
2173
2225
  } from "@tabler/icons-react";
2174
2226
  import {
2175
- useState as useState9,
2227
+ useState as useState10,
2176
2228
  useEffect as useEffect6,
2177
2229
  useRef as useRef10,
2178
2230
  useCallback as useCallback11
@@ -2194,12 +2246,12 @@ function TableMenu({ className: _className }) {
2194
2246
  return null;
2195
2247
  }
2196
2248
  });
2197
- const [colGrips, setColGrips] = useState9([]);
2198
- const [rowGrips, setRowGrips] = useState9([]);
2199
- const [dropdown, setDropdown] = useState9(null);
2200
- const [tableRect, setTableRect] = useState9(null);
2201
- const [isHovering, setIsHovering] = useState9(false);
2202
- const [drag, setDrag] = useState9(null);
2249
+ const [colGrips, setColGrips] = useState10([]);
2250
+ const [rowGrips, setRowGrips] = useState10([]);
2251
+ const [dropdown, setDropdown] = useState10(null);
2252
+ const [tableRect, setTableRect] = useState10(null);
2253
+ const [isHovering, setIsHovering] = useState10(false);
2254
+ const [drag, setDrag] = useState10(null);
2203
2255
  const dropdownRef = useRef10(null);
2204
2256
  const dragRef = useRef10(null);
2205
2257
  const getTableDom = useCallback11(() => {
@@ -2691,7 +2743,7 @@ function TableMenu({ className: _className }) {
2691
2743
  }
2692
2744
 
2693
2745
  // src/react/Editor.tsx
2694
- import { useMemo as useMemo2, useState as useState10, useCallback as useCallback12, useRef as useRef11, useEffect as useEffect7 } from "react";
2746
+ import { useMemo as useMemo2, useState as useState11, useCallback as useCallback12, useRef as useRef11, useEffect as useEffect7 } from "react";
2695
2747
  import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
2696
2748
  function Editor5({
2697
2749
  content,
@@ -2714,8 +2766,8 @@ function Editor5({
2714
2766
  slashCommand,
2715
2767
  placeholder
2716
2768
  }) {
2717
- const [actionMenuAnchor, setActionMenuAnchor] = useState10(null);
2718
- const [actionMenuEditor, setActionMenuEditor] = useState10(null);
2769
+ const [actionMenuAnchor, setActionMenuAnchor] = useState11(null);
2770
+ const [actionMenuEditor, setActionMenuEditor] = useState11(null);
2719
2771
  const actionMenuRef = useRef11(null);
2720
2772
  useEffect7(() => {
2721
2773
  if (!actionMenuAnchor) return;
@@ -2846,7 +2898,7 @@ function Editor5({
2846
2898
 
2847
2899
  // src/react/TableOfContents.tsx
2848
2900
  import { useCurrentEditor as useCurrentEditor6, useEditorState as useEditorState6 } from "@tiptap/react";
2849
- import { useCallback as useCallback13, useEffect as useEffect8, useRef as useRef12, useState as useState11 } from "react";
2901
+ import { useCallback as useCallback13, useEffect as useEffect8, useRef as useRef12, useState as useState12 } from "react";
2850
2902
  import { jsx as jsx17 } from "react/jsx-runtime";
2851
2903
  function TableOfContents({
2852
2904
  className,
@@ -2854,7 +2906,7 @@ function TableOfContents({
2854
2906
  activeClassName
2855
2907
  }) {
2856
2908
  const { editor } = useCurrentEditor6();
2857
- const [activeId, setActiveId] = useState11(null);
2909
+ const [activeId, setActiveId] = useState12(null);
2858
2910
  const observerRef = useRef12(null);
2859
2911
  const headings = useEditorState6({
2860
2912
  editor,