@tnd028/strapi-plugin-tiptap-editor 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/_chunks/{Alignment-qMOzO-n2.mjs → Alignment-BTrqlTTh.mjs} +1 -1
  2. package/dist/_chunks/{Alignment-B0f2-LO0.js → Alignment-hS-tTWuJ.js} +1 -1
  3. package/dist/_chunks/{Blockquote-9wBGDz3L.mjs → Blockquote-DJUK1-BE.mjs} +1 -1
  4. package/dist/_chunks/{Blockquote-DeGUfK27.js → Blockquote-xNUeALuh.js} +1 -1
  5. package/dist/_chunks/{Bold-C-0g6Ayd.mjs → Bold-ByaAbROo.mjs} +1 -1
  6. package/dist/_chunks/{Bold-ClnPsPHt.js → Bold-CueKtObk.js} +1 -1
  7. package/dist/_chunks/{ColorAndHighlight-t1Ui5tcL.mjs → ColorAndHighlight-D53O4qn1.mjs} +6 -2
  8. package/dist/_chunks/{ColorAndHighlight-BMmtoSpj.js → ColorAndHighlight-DW64XLWJ.js} +6 -2
  9. package/dist/_chunks/CustomCard-DnOBubyS.mjs +151 -0
  10. package/dist/_chunks/CustomCard-pj8tTTbU.js +153 -0
  11. package/dist/_chunks/{Emoji-C-LQZ4Z3.js → Emoji-CEoQDS_x.js} +1 -1
  12. package/dist/_chunks/{Emoji-Bm1_vSh5.mjs → Emoji-CSN4sPHu.mjs} +1 -1
  13. package/dist/_chunks/{Heading-BDJqd1Qw.js → Heading-DMpM8l4P.js} +1 -1
  14. package/dist/_chunks/{Heading-BycRob5M.mjs → Heading-DV6F-wwl.mjs} +1 -1
  15. package/dist/_chunks/{HorizontalRule-CJlDmdma.js → HorizontalRule-BkDBP3Sn.js} +1 -1
  16. package/dist/_chunks/{HorizontalRule-CzQCeC3b.mjs → HorizontalRule-duJS5mkv.mjs} +1 -1
  17. package/dist/_chunks/{Iframe-DCBWSb0v.mjs → Iframe-BrjBTqYD.mjs} +1 -1
  18. package/dist/_chunks/{Iframe-Dyn_2cMB.js → Iframe-jTxffChk.js} +1 -1
  19. package/dist/_chunks/{ImagePlaceholder-DtrSuhf_.mjs → ImagePlaceholder-CYDho-h0.mjs} +1 -1
  20. package/dist/_chunks/{ImagePlaceholder-C14kojd5.js → ImagePlaceholder-Da68VBcm.js} +1 -1
  21. package/dist/_chunks/{Italic-BfpKX0KX.mjs → Italic-C0EqNpkt.mjs} +1 -1
  22. package/dist/_chunks/{Italic-DdhVL78v.js → Italic-DxsFIMhE.js} +1 -1
  23. package/dist/_chunks/{Link-DUV5Ybep.mjs → Link-CIkiGBxD.mjs} +1 -1
  24. package/dist/_chunks/{Link-BuYDqSqo.js → Link-u4PGWt-U.js} +1 -1
  25. package/dist/_chunks/{Lists-DsKxj3MD.mjs → Lists-B6BSkd5d.mjs} +1 -1
  26. package/dist/_chunks/{Lists-BKopoFR6.js → Lists-Bv_GF1oJ.js} +1 -1
  27. package/dist/_chunks/{Redo-1mEX1yOd.js → Redo-DUNTAtjb.js} +1 -1
  28. package/dist/_chunks/{Redo-Ca3VQxSe.mjs → Redo-De5pQoO6.mjs} +1 -1
  29. package/dist/_chunks/{SearchAndReplace-DzNdbTWJ.js → SearchAndReplace-DtSE0ubu.js} +1 -1
  30. package/dist/_chunks/{SearchAndReplace-DmU5DlWv.mjs → SearchAndReplace-QP8H8yFv.mjs} +1 -1
  31. package/dist/_chunks/{Strikethrough-BygxCHak.mjs → Strikethrough-BsanR8PL.mjs} +1 -1
  32. package/dist/_chunks/{Strikethrough-DQfR4OFk.js → Strikethrough-DoetuFNk.js} +1 -1
  33. package/dist/_chunks/{Table-DLaqBAbz.js → Table-B5VeLc9S.js} +1 -1
  34. package/dist/_chunks/{Table-BhbZ2Blv.mjs → Table-DevGt3tV.mjs} +1 -1
  35. package/dist/_chunks/{TiptapEditorInput-vfLsCt1p.js → TiptapEditorInput-CArezP4p.js} +96 -30
  36. package/dist/_chunks/{TiptapEditorInput-BDE09h5-.mjs → TiptapEditorInput-ChH1R_k4.mjs} +96 -30
  37. package/dist/_chunks/{Undo-BuT4OkoE.js → Undo-Bp_GQXYz.js} +1 -1
  38. package/dist/_chunks/{Undo-PL5n4axf.mjs → Undo-D7dSauIG.mjs} +1 -1
  39. package/dist/_chunks/{index-ku90UbIF.mjs → index-2RQ7z9sc.mjs} +9 -3
  40. package/dist/_chunks/{index-wYDv8vsJ.js → index-B6IeUCyh.js} +9 -3
  41. package/dist/admin/index.js +1 -1
  42. package/dist/admin/index.mjs +1 -1
  43. package/dist/admin/src/components/editor/partials/ToolbarButtons.d.ts +1 -0
  44. package/dist/admin/src/components/editor/toolbars/CustomCard.d.ts +4 -0
  45. package/dist/admin/src/types.d.ts +1 -1
  46. package/dist/server/index.js +10 -4
  47. package/dist/server/index.mjs +10 -4
  48. package/package.json +2 -2
@@ -1,7 +1,7 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import { Table2, Table, ArrowUp, ArrowDown, ArrowLeft, ArrowRight, Rows3, Columns3, Trash2 } from "lucide-react";
3
3
  import styled from "styled-components";
4
- import { u as useEditorContext, D as DropdownMenu, a as DropdownMenuTrigger, T as ToolbarButton, b as DropdownMenuContent, c as DropdownMenuGroup, d as DropdownMenuItem, g as TooltipContent } from "./TiptapEditorInput-BDE09h5-.mjs";
4
+ import { u as useEditorContext, D as DropdownMenu, a as DropdownMenuTrigger, T as ToolbarButton, b as DropdownMenuContent, c as DropdownMenuGroup, d as DropdownMenuItem, g as TooltipContent } from "./TiptapEditorInput-ChH1R_k4.mjs";
5
5
  const IconWrapper = styled.span`
6
6
  display: flex;
7
7
  align-items: center;
@@ -46,7 +46,7 @@ const clsx = require("clsx");
46
46
  const Heading = require("@tiptap/extension-heading");
47
47
  const designSystem = require("@strapi/design-system");
48
48
  const Suggestion = require("@tiptap/suggestion");
49
- const index = require("./index-wYDv8vsJ.js");
49
+ const index = require("./index-B6IeUCyh.js");
50
50
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
51
51
  function _interopNamespace(e) {
52
52
  if (e && e.__esModule) return e;
@@ -431,6 +431,61 @@ const Button = React.forwardRef(
431
431
  }
432
432
  );
433
433
  Button.displayName = "Button";
434
+ function normalizeYoutubeToEmbed(raw) {
435
+ const input = (raw ?? "").trim();
436
+ if (!input) return input;
437
+ if (/^https?:\/\/(www\.)?youtube\.com\/embed\//i.test(input)) {
438
+ return input;
439
+ }
440
+ let url;
441
+ try {
442
+ url = new URL(input);
443
+ } catch {
444
+ return input;
445
+ }
446
+ const host = url.hostname.replace(/^www\./i, "").toLowerCase();
447
+ const isYoutubeHost = host === "youtube.com" || host === "m.youtube.com";
448
+ const isYoutuBeHost = host === "youtu.be";
449
+ if (!isYoutubeHost && !isYoutuBeHost) return input;
450
+ let videoId = null;
451
+ if (isYoutuBeHost) {
452
+ videoId = url.pathname.split("/").filter(Boolean)[0] ?? null;
453
+ } else {
454
+ const pathname = url.pathname;
455
+ if (pathname === "/watch") {
456
+ videoId = url.searchParams.get("v");
457
+ } else if (pathname.startsWith("/shorts/")) {
458
+ videoId = pathname.split("/").filter(Boolean)[1] ?? null;
459
+ } else if (pathname.startsWith("/live/")) {
460
+ videoId = pathname.split("/").filter(Boolean)[1] ?? null;
461
+ } else if (pathname.startsWith("/embed/")) {
462
+ videoId = pathname.split("/").filter(Boolean)[1] ?? null;
463
+ }
464
+ }
465
+ if (!videoId) return input;
466
+ const embed = new URL(`https://www.youtube.com/embed/${videoId}`);
467
+ const list = url.searchParams.get("list");
468
+ const start2 = url.searchParams.get("start") ?? url.searchParams.get("t");
469
+ if (list) embed.searchParams.set("list", list);
470
+ if (start2) {
471
+ const seconds = parseYoutubeTimeToSeconds(start2);
472
+ embed.searchParams.set("start", seconds != null ? String(seconds) : start2);
473
+ }
474
+ return embed.toString();
475
+ }
476
+ function parseYoutubeTimeToSeconds(value) {
477
+ const v = (value ?? "").trim();
478
+ if (!v) return null;
479
+ if (/^\d+$/.test(v)) return Number(v);
480
+ const match = v.match(/^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/i);
481
+ if (!match) return null;
482
+ const [, h, m, s] = match;
483
+ const hours = h ? Number(h) : 0;
484
+ const minutes = m ? Number(m) : 0;
485
+ const seconds = s ? Number(s) : 0;
486
+ const total = hours * 3600 + minutes * 60 + seconds;
487
+ return Number.isFinite(total) && total > 0 ? total : null;
488
+ }
434
489
  const Iframe = core.Node.create({
435
490
  name: "iframe",
436
491
  group: "block",
@@ -476,7 +531,10 @@ const Iframe = core.Node.create({
476
531
  return {
477
532
  setIframe: (options) => ({ tr, dispatch }) => {
478
533
  const { selection } = tr;
479
- const node = this.type.create(options);
534
+ const node = this.type.create({
535
+ ...options,
536
+ src: normalizeYoutubeToEmbed(options.src)
537
+ });
480
538
  if (dispatch) {
481
539
  tr.replaceRangeWith(selection.from, selection.to, node);
482
540
  }
@@ -492,7 +550,7 @@ function TiptapIframeComponent({ node, selected, deleteNode, updateAttributes })
492
550
  const [isEditing, setIsEditing] = React.useState(false);
493
551
  const [tempSrc, setTempSrc] = React.useState(node.attrs.src || "");
494
552
  const handleSave = () => {
495
- updateAttributes({ src: tempSrc });
553
+ updateAttributes({ src: normalizeYoutubeToEmbed(tempSrc) });
496
554
  setIsEditing(false);
497
555
  };
498
556
  return /* @__PURE__ */ jsxRuntime.jsxs(IframeWrapper, { $selected: selected, children: [
@@ -551,7 +609,6 @@ const IframeWrapper = styled__default.default(react.NodeViewWrapper)`
551
609
  width: 100%;
552
610
  min-height: 300px;
553
611
  border: none;
554
- border-radius: 4px;
555
612
  }
556
613
 
557
614
  input {
@@ -570,7 +627,6 @@ const Placeholder = styled__default.default.div`
570
627
  color: #999;
571
628
  font-style: italic;
572
629
  background: #fafafa;
573
- border-radius: 4px;
574
630
  `;
575
631
  function safelyResetPointerEvents(delay = 100) {
576
632
  requestAnimationFrame(() => {
@@ -1814,92 +1870,97 @@ const StyledSeparator = styled__default.default(Separator)`
1814
1870
  `;
1815
1871
  const ToolbarDefinitions = {
1816
1872
  // === GRUP 1: HEADING & TEXT STYLE ===
1873
+ customCard: React.lazy(
1874
+ () => Promise.resolve().then(() => require("./CustomCard-pj8tTTbU.js")).then((mod) => ({
1875
+ default: mod.CustomCardToolbar
1876
+ }))
1877
+ ),
1817
1878
  heading: React.lazy(
1818
- () => Promise.resolve().then(() => require("./Heading-BDJqd1Qw.js")).then((mod) => ({
1879
+ () => Promise.resolve().then(() => require("./Heading-DMpM8l4P.js")).then((mod) => ({
1819
1880
  default: mod.HeadingToolbar
1820
1881
  }))
1821
1882
  ),
1822
1883
  bold: React.lazy(
1823
- () => Promise.resolve().then(() => require("./Bold-ClnPsPHt.js")).then((mod) => ({
1884
+ () => Promise.resolve().then(() => require("./Bold-CueKtObk.js")).then((mod) => ({
1824
1885
  default: mod.BoldToolbar
1825
1886
  }))
1826
1887
  ),
1827
1888
  italic: React.lazy(
1828
- () => Promise.resolve().then(() => require("./Italic-DdhVL78v.js")).then((mod) => ({
1889
+ () => Promise.resolve().then(() => require("./Italic-DxsFIMhE.js")).then((mod) => ({
1829
1890
  default: mod.ItalicToolbar
1830
1891
  }))
1831
1892
  ),
1832
1893
  strikethrough: React.lazy(
1833
- () => Promise.resolve().then(() => require("./Strikethrough-DQfR4OFk.js")).then((mod) => ({
1894
+ () => Promise.resolve().then(() => require("./Strikethrough-DoetuFNk.js")).then((mod) => ({
1834
1895
  default: mod.StrikeThroughToolbar
1835
1896
  }))
1836
1897
  ),
1837
1898
  // === GRUP 2: LISTS & INDENTATION ===
1838
1899
  lists: React.lazy(
1839
- () => Promise.resolve().then(() => require("./Lists-BKopoFR6.js")).then((mod) => ({
1900
+ () => Promise.resolve().then(() => require("./Lists-Bv_GF1oJ.js")).then((mod) => ({
1840
1901
  default: mod.ListsToolbar
1841
1902
  }))
1842
1903
  ),
1843
1904
  blockquote: React.lazy(
1844
- () => Promise.resolve().then(() => require("./Blockquote-DeGUfK27.js")).then((mod) => ({
1905
+ () => Promise.resolve().then(() => require("./Blockquote-xNUeALuh.js")).then((mod) => ({
1845
1906
  default: mod.BlockquoteToolbar
1846
1907
  }))
1847
1908
  ),
1848
1909
  // === GRUP 3: ALIGNMENT & FORMATTING ===
1849
1910
  alignment: React.lazy(
1850
- () => Promise.resolve().then(() => require("./Alignment-B0f2-LO0.js")).then((mod) => ({
1911
+ () => Promise.resolve().then(() => require("./Alignment-hS-tTWuJ.js")).then((mod) => ({
1851
1912
  default: mod.AlignmentToolbar
1852
1913
  }))
1853
1914
  ),
1854
1915
  horizontalRule: React.lazy(
1855
- () => Promise.resolve().then(() => require("./HorizontalRule-CJlDmdma.js")).then((mod) => ({
1916
+ () => Promise.resolve().then(() => require("./HorizontalRule-BkDBP3Sn.js")).then((mod) => ({
1856
1917
  default: mod.HorizontalRuleToolbar
1857
1918
  }))
1858
1919
  ),
1859
1920
  color: React.lazy(
1860
- () => Promise.resolve().then(() => require("./ColorAndHighlight-BMmtoSpj.js")).then((mod) => ({
1921
+ () => Promise.resolve().then(() => require("./ColorAndHighlight-DW64XLWJ.js")).then((mod) => ({
1861
1922
  default: mod.ColorAndHighlightToolbar
1862
1923
  }))
1863
1924
  ),
1864
1925
  // === GRUP 4: MEDIA & LINKS ===
1865
1926
  link: React.lazy(
1866
- () => Promise.resolve().then(() => require("./Link-BuYDqSqo.js")).then((mod) => ({
1927
+ () => Promise.resolve().then(() => require("./Link-u4PGWt-U.js")).then((mod) => ({
1867
1928
  default: mod.LinkToolbar
1868
1929
  }))
1869
1930
  ),
1870
1931
  image: React.lazy(
1871
- () => Promise.resolve().then(() => require("./ImagePlaceholder-C14kojd5.js")).then((mod) => ({
1932
+ () => Promise.resolve().then(() => require("./ImagePlaceholder-Da68VBcm.js")).then((mod) => ({
1872
1933
  default: mod.ImagePlaceholderToolbar
1873
1934
  }))
1874
1935
  ),
1875
1936
  emoji: React.lazy(
1876
- () => Promise.resolve().then(() => require("./Emoji-C-LQZ4Z3.js")).then((mod) => ({
1937
+ () => Promise.resolve().then(() => require("./Emoji-CEoQDS_x.js")).then((mod) => ({
1877
1938
  default: mod.EmojiToolbar
1878
1939
  }))
1879
1940
  ),
1880
1941
  table: React.lazy(
1881
- () => Promise.resolve().then(() => require("./Table-DLaqBAbz.js")).then((mod) => ({
1942
+ () => Promise.resolve().then(() => require("./Table-B5VeLc9S.js")).then((mod) => ({
1882
1943
  default: mod.TableToolbar
1883
1944
  }))
1884
1945
  ),
1885
1946
  iframe: React.lazy(
1886
- () => Promise.resolve().then(() => require("./Iframe-Dyn_2cMB.js")).then((mod) => ({
1947
+ () => Promise.resolve().then(() => require("./Iframe-jTxffChk.js")).then((mod) => ({
1887
1948
  default: mod.IframeToolbar
1888
1949
  }))
1889
1950
  ),
1890
1951
  // === GRUP 5: UTILS ===
1891
1952
  undo: React.lazy(
1892
- () => Promise.resolve().then(() => require("./Undo-BuT4OkoE.js")).then((mod) => ({
1953
+ () => Promise.resolve().then(() => require("./Undo-Bp_GQXYz.js")).then((mod) => ({
1893
1954
  default: mod.UndoToolbar
1894
1955
  }))
1895
1956
  ),
1896
1957
  redo: React.lazy(
1897
- () => Promise.resolve().then(() => require("./Redo-1mEX1yOd.js")).then((mod) => ({
1958
+ () => Promise.resolve().then(() => require("./Redo-DUNTAtjb.js")).then((mod) => ({
1898
1959
  default: mod.RedoToolbar
1899
1960
  }))
1900
1961
  ),
1901
1962
  searchAndReplace: React.lazy(
1902
- () => Promise.resolve().then(() => require("./SearchAndReplace-DzNdbTWJ.js")).then((mod) => ({
1963
+ () => Promise.resolve().then(() => require("./SearchAndReplace-DtSE0ubu.js")).then((mod) => ({
1903
1964
  default: mod.SearchAndReplaceToolbar
1904
1965
  }))
1905
1966
  )
@@ -2619,9 +2680,13 @@ const createExtensions = () => [
2619
2680
  TextStyle__default.default
2620
2681
  ];
2621
2682
  const getOutput = (editor, output) => {
2622
- {
2683
+ if (output === "html") {
2684
+ return editor.getHTML();
2685
+ }
2686
+ if (output === "json") {
2623
2687
  return editor.getJSON();
2624
2688
  }
2689
+ return editor.getText();
2625
2690
  };
2626
2691
  const useEditor = ({
2627
2692
  content,
@@ -2640,7 +2705,7 @@ const useEditor = ({
2640
2705
  }, throttleDelay);
2641
2706
  const handleUpdate = React.useCallback(
2642
2707
  (editor2) => {
2643
- return throttledSetValue(getOutput(editor2));
2708
+ return throttledSetValue(getOutput(editor2, output));
2644
2709
  },
2645
2710
  // eslint-disable-next-line react-hooks/exhaustive-deps
2646
2711
  [output]
@@ -2654,7 +2719,7 @@ const useEditor = ({
2654
2719
  [value]
2655
2720
  );
2656
2721
  const handleBlur = React.useCallback(
2657
- (editor2) => onBlur?.(getOutput(editor2)),
2722
+ (editor2) => onBlur?.(getOutput(editor2, output)),
2658
2723
  [output, onBlur]
2659
2724
  );
2660
2725
  const editor = react.useEditor({
@@ -3500,7 +3565,7 @@ const BaseEditor = React.forwardRef(
3500
3565
  onUpdate: onChange,
3501
3566
  value,
3502
3567
  editable: !disabled,
3503
- output: "json"
3568
+ output: options.output ?? "html"
3504
3569
  });
3505
3570
  React.useImperativeHandle(ref, () => editor, [editor]);
3506
3571
  if (!editor) {
@@ -3708,7 +3773,7 @@ const CustomComponentRenderer = (props) => {
3708
3773
  }
3709
3774
  };
3710
3775
  return /* @__PURE__ */ jsxRuntime.jsxs(Wrapper, { $selected: selected, children: [
3711
- /* @__PURE__ */ jsxRuntime.jsx(Badge, { $type: node.attrs.type, children: node.attrs.type.replace("custom", "") }),
3776
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { $type: node.attrs.type, children: node.attrs.type?.replace("custom", "") }),
3712
3777
  renderPreview(),
3713
3778
  editor.isEditable && /* @__PURE__ */ jsxRuntime.jsxs(Toolbar, { children: [
3714
3779
  /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: handleEdit, style: { width: 28, height: 28, border: "1px solid #ddd", borderRadius: 4, background: "white", display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit3, { size: 14 }) }),
@@ -6595,7 +6660,8 @@ function ContentEditor({
6595
6660
  "table",
6596
6661
  "image",
6597
6662
  "iframe",
6598
- "emoji"
6663
+ "emoji",
6664
+ "customCard"
6599
6665
  // "HTMLToolbar"
6600
6666
  ],
6601
6667
  extensions = [],
@@ -6698,7 +6764,7 @@ const ContentEditorInput = React__default.default.memo(React__default.default.fo
6698
6764
  target: {
6699
6765
  name,
6700
6766
  value: content,
6701
- type: "json"
6767
+ type: "text"
6702
6768
  }
6703
6769
  };
6704
6770
  onChange(evt);
@@ -6789,7 +6855,7 @@ const TextEditorInput = React__default.default.memo(React__default.default.forwa
6789
6855
  target: {
6790
6856
  name,
6791
6857
  value: content,
6792
- type: "json"
6858
+ type: "text"
6793
6859
  }
6794
6860
  };
6795
6861
  onChange(evt);
@@ -45,7 +45,7 @@ import { clsx } from "clsx";
45
45
  import Heading from "@tiptap/extension-heading";
46
46
  import { Checkbox, Field, Flex } from "@strapi/design-system";
47
47
  import Suggestion from "@tiptap/suggestion";
48
- import { g as getPluginConfig } from "./index-ku90UbIF.mjs";
48
+ import { g as getPluginConfig } from "./index-2RQ7z9sc.mjs";
49
49
  const validateFileOrBase64 = (input, options, originalFile, validFiles, errors) => {
50
50
  const { isValidType, isValidSize } = checkTypeAndSize(input, options);
51
51
  if (isValidType && isValidSize) {
@@ -377,6 +377,61 @@ const Button = forwardRef(
377
377
  }
378
378
  );
379
379
  Button.displayName = "Button";
380
+ function normalizeYoutubeToEmbed(raw) {
381
+ const input = (raw ?? "").trim();
382
+ if (!input) return input;
383
+ if (/^https?:\/\/(www\.)?youtube\.com\/embed\//i.test(input)) {
384
+ return input;
385
+ }
386
+ let url;
387
+ try {
388
+ url = new URL(input);
389
+ } catch {
390
+ return input;
391
+ }
392
+ const host = url.hostname.replace(/^www\./i, "").toLowerCase();
393
+ const isYoutubeHost = host === "youtube.com" || host === "m.youtube.com";
394
+ const isYoutuBeHost = host === "youtu.be";
395
+ if (!isYoutubeHost && !isYoutuBeHost) return input;
396
+ let videoId = null;
397
+ if (isYoutuBeHost) {
398
+ videoId = url.pathname.split("/").filter(Boolean)[0] ?? null;
399
+ } else {
400
+ const pathname = url.pathname;
401
+ if (pathname === "/watch") {
402
+ videoId = url.searchParams.get("v");
403
+ } else if (pathname.startsWith("/shorts/")) {
404
+ videoId = pathname.split("/").filter(Boolean)[1] ?? null;
405
+ } else if (pathname.startsWith("/live/")) {
406
+ videoId = pathname.split("/").filter(Boolean)[1] ?? null;
407
+ } else if (pathname.startsWith("/embed/")) {
408
+ videoId = pathname.split("/").filter(Boolean)[1] ?? null;
409
+ }
410
+ }
411
+ if (!videoId) return input;
412
+ const embed = new URL(`https://www.youtube.com/embed/${videoId}`);
413
+ const list = url.searchParams.get("list");
414
+ const start2 = url.searchParams.get("start") ?? url.searchParams.get("t");
415
+ if (list) embed.searchParams.set("list", list);
416
+ if (start2) {
417
+ const seconds = parseYoutubeTimeToSeconds(start2);
418
+ embed.searchParams.set("start", seconds != null ? String(seconds) : start2);
419
+ }
420
+ return embed.toString();
421
+ }
422
+ function parseYoutubeTimeToSeconds(value) {
423
+ const v = (value ?? "").trim();
424
+ if (!v) return null;
425
+ if (/^\d+$/.test(v)) return Number(v);
426
+ const match = v.match(/^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/i);
427
+ if (!match) return null;
428
+ const [, h, m, s] = match;
429
+ const hours = h ? Number(h) : 0;
430
+ const minutes = m ? Number(m) : 0;
431
+ const seconds = s ? Number(s) : 0;
432
+ const total = hours * 3600 + minutes * 60 + seconds;
433
+ return Number.isFinite(total) && total > 0 ? total : null;
434
+ }
380
435
  const Iframe = Node.create({
381
436
  name: "iframe",
382
437
  group: "block",
@@ -422,7 +477,10 @@ const Iframe = Node.create({
422
477
  return {
423
478
  setIframe: (options) => ({ tr, dispatch }) => {
424
479
  const { selection } = tr;
425
- const node = this.type.create(options);
480
+ const node = this.type.create({
481
+ ...options,
482
+ src: normalizeYoutubeToEmbed(options.src)
483
+ });
426
484
  if (dispatch) {
427
485
  tr.replaceRangeWith(selection.from, selection.to, node);
428
486
  }
@@ -438,7 +496,7 @@ function TiptapIframeComponent({ node, selected, deleteNode, updateAttributes })
438
496
  const [isEditing, setIsEditing] = useState(false);
439
497
  const [tempSrc, setTempSrc] = useState(node.attrs.src || "");
440
498
  const handleSave = () => {
441
- updateAttributes({ src: tempSrc });
499
+ updateAttributes({ src: normalizeYoutubeToEmbed(tempSrc) });
442
500
  setIsEditing(false);
443
501
  };
444
502
  return /* @__PURE__ */ jsxs(IframeWrapper, { $selected: selected, children: [
@@ -497,7 +555,6 @@ const IframeWrapper = styled(NodeViewWrapper)`
497
555
  width: 100%;
498
556
  min-height: 300px;
499
557
  border: none;
500
- border-radius: 4px;
501
558
  }
502
559
 
503
560
  input {
@@ -516,7 +573,6 @@ const Placeholder = styled.div`
516
573
  color: #999;
517
574
  font-style: italic;
518
575
  background: #fafafa;
519
- border-radius: 4px;
520
576
  `;
521
577
  function safelyResetPointerEvents(delay = 100) {
522
578
  requestAnimationFrame(() => {
@@ -1760,92 +1816,97 @@ const StyledSeparator = styled(Separator)`
1760
1816
  `;
1761
1817
  const ToolbarDefinitions = {
1762
1818
  // === GRUP 1: HEADING & TEXT STYLE ===
1819
+ customCard: lazy(
1820
+ () => import("./CustomCard-DnOBubyS.mjs").then((mod) => ({
1821
+ default: mod.CustomCardToolbar
1822
+ }))
1823
+ ),
1763
1824
  heading: lazy(
1764
- () => import("./Heading-BycRob5M.mjs").then((mod) => ({
1825
+ () => import("./Heading-DV6F-wwl.mjs").then((mod) => ({
1765
1826
  default: mod.HeadingToolbar
1766
1827
  }))
1767
1828
  ),
1768
1829
  bold: lazy(
1769
- () => import("./Bold-C-0g6Ayd.mjs").then((mod) => ({
1830
+ () => import("./Bold-ByaAbROo.mjs").then((mod) => ({
1770
1831
  default: mod.BoldToolbar
1771
1832
  }))
1772
1833
  ),
1773
1834
  italic: lazy(
1774
- () => import("./Italic-BfpKX0KX.mjs").then((mod) => ({
1835
+ () => import("./Italic-C0EqNpkt.mjs").then((mod) => ({
1775
1836
  default: mod.ItalicToolbar
1776
1837
  }))
1777
1838
  ),
1778
1839
  strikethrough: lazy(
1779
- () => import("./Strikethrough-BygxCHak.mjs").then((mod) => ({
1840
+ () => import("./Strikethrough-BsanR8PL.mjs").then((mod) => ({
1780
1841
  default: mod.StrikeThroughToolbar
1781
1842
  }))
1782
1843
  ),
1783
1844
  // === GRUP 2: LISTS & INDENTATION ===
1784
1845
  lists: lazy(
1785
- () => import("./Lists-DsKxj3MD.mjs").then((mod) => ({
1846
+ () => import("./Lists-B6BSkd5d.mjs").then((mod) => ({
1786
1847
  default: mod.ListsToolbar
1787
1848
  }))
1788
1849
  ),
1789
1850
  blockquote: lazy(
1790
- () => import("./Blockquote-9wBGDz3L.mjs").then((mod) => ({
1851
+ () => import("./Blockquote-DJUK1-BE.mjs").then((mod) => ({
1791
1852
  default: mod.BlockquoteToolbar
1792
1853
  }))
1793
1854
  ),
1794
1855
  // === GRUP 3: ALIGNMENT & FORMATTING ===
1795
1856
  alignment: lazy(
1796
- () => import("./Alignment-qMOzO-n2.mjs").then((mod) => ({
1857
+ () => import("./Alignment-BTrqlTTh.mjs").then((mod) => ({
1797
1858
  default: mod.AlignmentToolbar
1798
1859
  }))
1799
1860
  ),
1800
1861
  horizontalRule: lazy(
1801
- () => import("./HorizontalRule-CzQCeC3b.mjs").then((mod) => ({
1862
+ () => import("./HorizontalRule-duJS5mkv.mjs").then((mod) => ({
1802
1863
  default: mod.HorizontalRuleToolbar
1803
1864
  }))
1804
1865
  ),
1805
1866
  color: lazy(
1806
- () => import("./ColorAndHighlight-t1Ui5tcL.mjs").then((mod) => ({
1867
+ () => import("./ColorAndHighlight-D53O4qn1.mjs").then((mod) => ({
1807
1868
  default: mod.ColorAndHighlightToolbar
1808
1869
  }))
1809
1870
  ),
1810
1871
  // === GRUP 4: MEDIA & LINKS ===
1811
1872
  link: lazy(
1812
- () => import("./Link-DUV5Ybep.mjs").then((mod) => ({
1873
+ () => import("./Link-CIkiGBxD.mjs").then((mod) => ({
1813
1874
  default: mod.LinkToolbar
1814
1875
  }))
1815
1876
  ),
1816
1877
  image: lazy(
1817
- () => import("./ImagePlaceholder-DtrSuhf_.mjs").then((mod) => ({
1878
+ () => import("./ImagePlaceholder-CYDho-h0.mjs").then((mod) => ({
1818
1879
  default: mod.ImagePlaceholderToolbar
1819
1880
  }))
1820
1881
  ),
1821
1882
  emoji: lazy(
1822
- () => import("./Emoji-Bm1_vSh5.mjs").then((mod) => ({
1883
+ () => import("./Emoji-CSN4sPHu.mjs").then((mod) => ({
1823
1884
  default: mod.EmojiToolbar
1824
1885
  }))
1825
1886
  ),
1826
1887
  table: lazy(
1827
- () => import("./Table-BhbZ2Blv.mjs").then((mod) => ({
1888
+ () => import("./Table-DevGt3tV.mjs").then((mod) => ({
1828
1889
  default: mod.TableToolbar
1829
1890
  }))
1830
1891
  ),
1831
1892
  iframe: lazy(
1832
- () => import("./Iframe-DCBWSb0v.mjs").then((mod) => ({
1893
+ () => import("./Iframe-BrjBTqYD.mjs").then((mod) => ({
1833
1894
  default: mod.IframeToolbar
1834
1895
  }))
1835
1896
  ),
1836
1897
  // === GRUP 5: UTILS ===
1837
1898
  undo: lazy(
1838
- () => import("./Undo-PL5n4axf.mjs").then((mod) => ({
1899
+ () => import("./Undo-D7dSauIG.mjs").then((mod) => ({
1839
1900
  default: mod.UndoToolbar
1840
1901
  }))
1841
1902
  ),
1842
1903
  redo: lazy(
1843
- () => import("./Redo-Ca3VQxSe.mjs").then((mod) => ({
1904
+ () => import("./Redo-De5pQoO6.mjs").then((mod) => ({
1844
1905
  default: mod.RedoToolbar
1845
1906
  }))
1846
1907
  ),
1847
1908
  searchAndReplace: lazy(
1848
- () => import("./SearchAndReplace-DmU5DlWv.mjs").then((mod) => ({
1909
+ () => import("./SearchAndReplace-QP8H8yFv.mjs").then((mod) => ({
1849
1910
  default: mod.SearchAndReplaceToolbar
1850
1911
  }))
1851
1912
  )
@@ -2565,9 +2626,13 @@ const createExtensions = () => [
2565
2626
  TextStyle
2566
2627
  ];
2567
2628
  const getOutput = (editor, output) => {
2568
- {
2629
+ if (output === "html") {
2630
+ return editor.getHTML();
2631
+ }
2632
+ if (output === "json") {
2569
2633
  return editor.getJSON();
2570
2634
  }
2635
+ return editor.getText();
2571
2636
  };
2572
2637
  const useEditor = ({
2573
2638
  content,
@@ -2586,7 +2651,7 @@ const useEditor = ({
2586
2651
  }, throttleDelay);
2587
2652
  const handleUpdate = useCallback(
2588
2653
  (editor2) => {
2589
- return throttledSetValue(getOutput(editor2));
2654
+ return throttledSetValue(getOutput(editor2, output));
2590
2655
  },
2591
2656
  // eslint-disable-next-line react-hooks/exhaustive-deps
2592
2657
  [output]
@@ -2600,7 +2665,7 @@ const useEditor = ({
2600
2665
  [value]
2601
2666
  );
2602
2667
  const handleBlur = useCallback(
2603
- (editor2) => onBlur?.(getOutput(editor2)),
2668
+ (editor2) => onBlur?.(getOutput(editor2, output)),
2604
2669
  [output, onBlur]
2605
2670
  );
2606
2671
  const editor = useEditor$1({
@@ -3446,7 +3511,7 @@ const BaseEditor = forwardRef(
3446
3511
  onUpdate: onChange,
3447
3512
  value,
3448
3513
  editable: !disabled,
3449
- output: "json"
3514
+ output: options.output ?? "html"
3450
3515
  });
3451
3516
  useImperativeHandle(ref, () => editor, [editor]);
3452
3517
  if (!editor) {
@@ -3654,7 +3719,7 @@ const CustomComponentRenderer = (props) => {
3654
3719
  }
3655
3720
  };
3656
3721
  return /* @__PURE__ */ jsxs(Wrapper, { $selected: selected, children: [
3657
- /* @__PURE__ */ jsx(Badge, { $type: node.attrs.type, children: node.attrs.type.replace("custom", "") }),
3722
+ /* @__PURE__ */ jsx(Badge, { $type: node.attrs.type, children: node.attrs.type?.replace("custom", "") }),
3658
3723
  renderPreview(),
3659
3724
  editor.isEditable && /* @__PURE__ */ jsxs(Toolbar, { children: [
3660
3725
  /* @__PURE__ */ jsx("button", { onClick: handleEdit, style: { width: 28, height: 28, border: "1px solid #ddd", borderRadius: 4, background: "white", display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx(Edit3, { size: 14 }) }),
@@ -6541,7 +6606,8 @@ function ContentEditor({
6541
6606
  "table",
6542
6607
  "image",
6543
6608
  "iframe",
6544
- "emoji"
6609
+ "emoji",
6610
+ "customCard"
6545
6611
  // "HTMLToolbar"
6546
6612
  ],
6547
6613
  extensions = [],
@@ -6644,7 +6710,7 @@ const ContentEditorInput = React.memo(React.forwardRef(
6644
6710
  target: {
6645
6711
  name,
6646
6712
  value: content,
6647
- type: "json"
6713
+ type: "text"
6648
6714
  }
6649
6715
  };
6650
6716
  onChange(evt);
@@ -6735,7 +6801,7 @@ const TextEditorInput = React.memo(React.forwardRef(
6735
6801
  target: {
6736
6802
  name,
6737
6803
  value: content,
6738
- type: "json"
6804
+ type: "text"
6739
6805
  }
6740
6806
  };
6741
6807
  onChange(evt);
@@ -3,7 +3,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const React = require("react");
5
5
  const lucideReact = require("lucide-react");
6
- const TiptapEditorInput = require("./TiptapEditorInput-vfLsCt1p.js");
6
+ const TiptapEditorInput = require("./TiptapEditorInput-CArezP4p.js");
7
7
  const styled = require("styled-components");
8
8
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
9
9
  const styled__default = /* @__PURE__ */ _interopDefault(styled);
@@ -1,7 +1,7 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { forwardRef } from "react";
3
3
  import { Undo2 } from "lucide-react";
4
- import { u as useEditorContext, T as ToolbarButton } from "./TiptapEditorInput-BDE09h5-.mjs";
4
+ import { u as useEditorContext, T as ToolbarButton } from "./TiptapEditorInput-ChH1R_k4.mjs";
5
5
  import styled from "styled-components";
6
6
  const UndoIcon = styled(Undo2)`
7
7
  width: 16px;
@@ -1031,11 +1031,17 @@ const colors = css`
1031
1031
  --tiptap-color-body: ${theme.colors.neutral800};
1032
1032
  --tiptap-color-heading: ${theme.colors.neutral900};
1033
1033
  --tiptap-color-muted: ${theme.colors.neutral500};
1034
+ --tiptap-color-muted-2: ${theme.colors.neutral600};
1034
1035
  --tiptap-color-border: ${theme.colors.neutral200};
1035
1036
  --tiptap-color-bg: ${theme.colors.neutral0};
1036
1037
  --tiptap-color-code-bg: ${theme.colors.neutral100};
1037
1038
  --tiptap-color-primary: ${theme.colors.primary600};
1038
1039
  --tiptap-color-ring: ${theme.colors.primary200};
1040
+
1041
+ /* Speaker card helpers */
1042
+ --tiptap-color-danger: ${theme.colors.danger500};
1043
+ --tiptap-color-desc-bg: ${theme.colors.primary700 ?? theme.colors.primary600};
1044
+ --tiptap-color-desc-fg: ${theme.colors.neutral0};
1039
1045
  }
1040
1046
  `}
1041
1047
  `;
@@ -1304,11 +1310,11 @@ const index = {
1304
1310
  * @param app - The Strapi administration application instance.
1305
1311
  */
1306
1312
  async register(app) {
1307
- console.log("Registering Tiptap Editor plugin (tiptap)...");
1313
+ console.log("Registering Tiptap Editor plugin (Tiptap)...");
1308
1314
  app.customFields.register({
1309
1315
  name: "tiptap",
1310
1316
  pluginId: PLUGIN_ID,
1311
- type: "json",
1317
+ type: "richtext",
1312
1318
  intlLabel: {
1313
1319
  id: `${PLUGIN_ID}.tiptap.label`,
1314
1320
  defaultMessage: "Tiptap Editor"
@@ -1319,7 +1325,7 @@ const index = {
1319
1325
  },
1320
1326
  icon: PluginIcon,
1321
1327
  components: {
1322
- Input: async () => import("./TiptapEditorInput-BDE09h5-.mjs").then((n) => n.i)
1328
+ Input: async () => import("./TiptapEditorInput-ChH1R_k4.mjs").then((n) => n.i)
1323
1329
  },
1324
1330
  options: {
1325
1331
  base: [