@kopexa/tiptap 17.0.17 → 17.2.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 (147) hide show
  1. package/dist/chunk-2U5CQUZH.mjs +91 -0
  2. package/dist/chunk-2V6VOAPI.mjs +139 -0
  3. package/dist/chunk-32SUXCAQ.mjs +115 -0
  4. package/dist/chunk-3VRQUYYW.mjs +169 -0
  5. package/dist/chunk-4CDZ547I.mjs +185 -0
  6. package/dist/chunk-5GFFTVMZ.mjs +62 -0
  7. package/dist/{chunk-FJAGUXEO.mjs → chunk-7SRL3P4B.mjs} +32 -19
  8. package/dist/{chunk-WHJ4B43N.mjs → chunk-7VGROP26.mjs} +69 -25
  9. package/dist/chunk-7VW67NVL.mjs +80 -0
  10. package/dist/chunk-BXHPO3T7.mjs +152 -0
  11. package/dist/{chunk-QF3YHPWM.mjs → chunk-E5NW3MJZ.mjs} +4 -4
  12. package/dist/chunk-FRJX2F4T.mjs +55 -0
  13. package/dist/chunk-IFXRPGIJ.mjs +98 -0
  14. package/dist/chunk-JCV5SEKN.mjs +65 -0
  15. package/dist/chunk-LMCQMSW2.mjs +345 -0
  16. package/dist/chunk-N4CT5RNC.mjs +123 -0
  17. package/dist/{chunk-B2DHYFSH.mjs → chunk-NEHW62L7.mjs} +56 -3
  18. package/dist/chunk-NSYSECKW.mjs +53 -0
  19. package/dist/{chunk-3IKIIRV3.mjs → chunk-QAE2D4KV.mjs} +39 -16
  20. package/dist/chunk-TAM3VMJT.mjs +80 -0
  21. package/dist/chunk-UU6JK5HX.mjs +257 -0
  22. package/dist/chunk-UVHVCION.mjs +168 -0
  23. package/dist/chunk-VF3G2URZ.mjs +83 -0
  24. package/dist/chunk-VRQ6OSAZ.mjs +76 -0
  25. package/dist/chunk-WAAH3NLG.mjs +77 -0
  26. package/dist/chunk-XNDXYI2N.mjs +158 -0
  27. package/dist/context/editor-file-context.d.mts +70 -0
  28. package/dist/context/editor-file-context.d.ts +70 -0
  29. package/dist/context/editor-file-context.js +96 -0
  30. package/dist/context/editor-file-context.mjs +12 -0
  31. package/dist/extensions/callout/callout-settings.d.mts +13 -0
  32. package/dist/extensions/callout/callout-settings.d.ts +13 -0
  33. package/dist/extensions/callout/callout-settings.js +206 -0
  34. package/dist/extensions/callout/callout-settings.mjs +9 -0
  35. package/dist/extensions/callout/callout-view.d.mts +12 -0
  36. package/dist/extensions/callout/callout-view.d.ts +12 -0
  37. package/dist/extensions/callout/callout-view.js +273 -0
  38. package/dist/extensions/callout/callout-view.mjs +12 -0
  39. package/dist/extensions/callout/index.d.mts +44 -0
  40. package/dist/extensions/callout/index.d.ts +44 -0
  41. package/dist/extensions/callout/index.js +380 -0
  42. package/dist/extensions/callout/index.mjs +13 -0
  43. package/dist/extensions/callout/messages.d.mts +59 -0
  44. package/dist/extensions/callout/messages.d.ts +59 -0
  45. package/dist/extensions/callout/messages.js +88 -0
  46. package/dist/extensions/callout/messages.mjs +7 -0
  47. package/dist/extensions/image/image-view.d.mts +15 -0
  48. package/dist/extensions/image/image-view.d.ts +15 -0
  49. package/dist/extensions/image/image-view.js +423 -0
  50. package/dist/extensions/image/image-view.mjs +12 -0
  51. package/dist/extensions/image/index.d.mts +66 -0
  52. package/dist/extensions/image/index.d.ts +66 -0
  53. package/dist/extensions/image/index.js +495 -0
  54. package/dist/extensions/image/index.mjs +16 -0
  55. package/dist/extensions/image/messages.d.mts +56 -0
  56. package/dist/extensions/image/messages.d.ts +56 -0
  57. package/dist/extensions/image/messages.js +85 -0
  58. package/dist/extensions/image/messages.mjs +7 -0
  59. package/dist/extensions/math/index.d.mts +38 -0
  60. package/dist/extensions/math/index.d.ts +38 -0
  61. package/dist/extensions/math/index.js +544 -0
  62. package/dist/extensions/math/index.mjs +17 -0
  63. package/dist/extensions/math/inline-math-view.d.mts +12 -0
  64. package/dist/extensions/math/inline-math-view.d.ts +12 -0
  65. package/dist/extensions/math/inline-math-view.js +232 -0
  66. package/dist/extensions/math/inline-math-view.mjs +11 -0
  67. package/dist/extensions/math/inline-math.d.mts +32 -0
  68. package/dist/extensions/math/inline-math.d.ts +32 -0
  69. package/dist/extensions/math/inline-math.js +304 -0
  70. package/dist/extensions/math/inline-math.mjs +12 -0
  71. package/dist/extensions/math/math-block-view.d.mts +11 -0
  72. package/dist/extensions/math/math-block-view.d.ts +11 -0
  73. package/dist/extensions/math/math-block-view.js +248 -0
  74. package/dist/extensions/math/math-block-view.mjs +11 -0
  75. package/dist/extensions/math/messages.d.mts +49 -0
  76. package/dist/extensions/math/messages.d.ts +49 -0
  77. package/dist/extensions/math/messages.js +78 -0
  78. package/dist/extensions/math/messages.mjs +7 -0
  79. package/dist/extensions/toc/index.d.mts +53 -0
  80. package/dist/extensions/toc/index.d.ts +53 -0
  81. package/dist/extensions/toc/index.js +501 -0
  82. package/dist/extensions/toc/index.mjs +13 -0
  83. package/dist/extensions/toc/messages.d.mts +74 -0
  84. package/dist/extensions/toc/messages.d.ts +74 -0
  85. package/dist/extensions/toc/messages.js +103 -0
  86. package/dist/extensions/toc/messages.mjs +7 -0
  87. package/dist/extensions/toc/toc-settings.d.mts +13 -0
  88. package/dist/extensions/toc/toc-settings.d.ts +13 -0
  89. package/dist/extensions/toc/toc-settings.js +283 -0
  90. package/dist/extensions/toc/toc-settings.mjs +9 -0
  91. package/dist/extensions/toc/toc-view.d.mts +12 -0
  92. package/dist/extensions/toc/toc-view.d.ts +12 -0
  93. package/dist/extensions/toc/toc-view.js +411 -0
  94. package/dist/extensions/toc/toc-view.mjs +12 -0
  95. package/dist/hooks/use-create-editor.d.mts +20 -2
  96. package/dist/hooks/use-create-editor.d.ts +20 -2
  97. package/dist/hooks/use-create-editor.js +2020 -20
  98. package/dist/hooks/use-create-editor.mjs +20 -3
  99. package/dist/index.d.mts +11 -2
  100. package/dist/index.d.ts +11 -2
  101. package/dist/index.js +3948 -1563
  102. package/dist/index.mjs +64 -23
  103. package/dist/presets/basic/editor-header.d.mts +3 -2
  104. package/dist/presets/basic/editor-header.d.ts +3 -2
  105. package/dist/presets/basic/editor-header.js +91 -25
  106. package/dist/presets/basic/editor-header.mjs +14 -14
  107. package/dist/presets/basic/index.d.mts +3 -1
  108. package/dist/presets/basic/index.d.ts +3 -1
  109. package/dist/presets/basic/index.js +3833 -1517
  110. package/dist/presets/basic/index.mjs +41 -22
  111. package/dist/ui/bubble-menu/index.d.mts +13 -0
  112. package/dist/ui/bubble-menu/index.d.ts +13 -0
  113. package/dist/ui/bubble-menu/index.js +671 -0
  114. package/dist/ui/bubble-menu/index.mjs +16 -0
  115. package/dist/ui/color-highlight-popover/color-highlight-popover.mjs +2 -2
  116. package/dist/ui/color-highlight-popover/index.mjs +2 -2
  117. package/dist/ui/copy-anchor-link-button/use-scroll-to-hash.mjs +2 -2
  118. package/dist/ui/link-bubble/index.d.mts +13 -0
  119. package/dist/ui/link-bubble/index.d.ts +13 -0
  120. package/dist/ui/link-bubble/index.js +182 -0
  121. package/dist/ui/link-bubble/index.mjs +10 -0
  122. package/dist/ui/link-popover/index.js +66 -23
  123. package/dist/ui/link-popover/index.mjs +3 -3
  124. package/dist/ui/link-popover/link-popover.d.mts +7 -2
  125. package/dist/ui/link-popover/link-popover.d.ts +7 -2
  126. package/dist/ui/link-popover/link-popover.js +66 -23
  127. package/dist/ui/link-popover/link-popover.mjs +3 -3
  128. package/dist/ui/link-popover/use-link-popover.mjs +2 -2
  129. package/dist/ui/slash-dropdown-menu/index.js +53 -3
  130. package/dist/ui/slash-dropdown-menu/index.mjs +6 -6
  131. package/dist/ui/slash-dropdown-menu/slash-dropdown-menu.js +53 -3
  132. package/dist/ui/slash-dropdown-menu/slash-dropdown-menu.mjs +4 -4
  133. package/dist/ui/slash-dropdown-menu/use-slash-dropdown-menu.d.mts +28 -0
  134. package/dist/ui/slash-dropdown-menu/use-slash-dropdown-menu.d.ts +28 -0
  135. package/dist/ui/slash-dropdown-menu/use-slash-dropdown-menu.js +53 -3
  136. package/dist/ui/slash-dropdown-menu/use-slash-dropdown-menu.mjs +1 -1
  137. package/dist/ui/suggestion-menu/index.mjs +2 -2
  138. package/dist/ui/suggestion-menu/suggestion-menu.mjs +2 -2
  139. package/dist/utils/safe-parse.js +113 -4
  140. package/dist/utils/safe-parse.mjs +1 -1
  141. package/package.json +48 -39
  142. package/dist/chunk-7LHOYNVF.mjs +0 -60
  143. package/dist/chunk-LXOLVGLW.mjs +0 -131
  144. package/dist/{chunk-XL5FS7LN.mjs → chunk-C5RQWJKE.mjs} +3 -3
  145. package/dist/{chunk-JNL4KY45.mjs → chunk-DZLGLP7R.mjs} +3 -3
  146. package/dist/{chunk-LHXRE26G.mjs → chunk-VTKJPVNM.mjs} +3 -3
  147. package/dist/{chunk-XLSZK3WJ.mjs → chunk-ZYFCSR3E.mjs} +3 -3
@@ -0,0 +1,91 @@
1
+ "use client";
2
+ import {
3
+ MathBlockView
4
+ } from "./chunk-UVHVCION.mjs";
5
+
6
+ // src/extensions/math/index.ts
7
+ import { InputRule, mergeAttributes, Node } from "@tiptap/core";
8
+ import { ReactNodeViewRenderer } from "@tiptap/react";
9
+ var MathBlock = Node.create({
10
+ name: "mathBlock",
11
+ group: "block",
12
+ atom: true,
13
+ draggable: true,
14
+ addOptions() {
15
+ return {
16
+ HTMLAttributes: {}
17
+ };
18
+ },
19
+ addAttributes() {
20
+ return {
21
+ latex: {
22
+ default: "",
23
+ parseHTML: (element) => element.getAttribute("data-latex") || "",
24
+ renderHTML: (attributes) => ({
25
+ "data-latex": attributes.latex
26
+ })
27
+ }
28
+ };
29
+ },
30
+ parseHTML() {
31
+ return [
32
+ {
33
+ tag: 'div[data-type="math-block"]'
34
+ }
35
+ ];
36
+ },
37
+ renderHTML({ HTMLAttributes }) {
38
+ return [
39
+ "div",
40
+ mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
41
+ "data-type": "math-block"
42
+ })
43
+ ];
44
+ },
45
+ addNodeView() {
46
+ return ReactNodeViewRenderer(MathBlockView);
47
+ },
48
+ addCommands() {
49
+ return {
50
+ insertMathBlock: (latex = "") => ({ commands }) => {
51
+ return commands.insertContent({
52
+ type: this.name,
53
+ attrs: { latex }
54
+ });
55
+ },
56
+ updateMathBlock: (latex) => ({ tr, state, dispatch }) => {
57
+ const { selection } = state;
58
+ const node = state.doc.nodeAt(selection.from);
59
+ if ((node == null ? void 0 : node.type.name) !== this.name) {
60
+ return false;
61
+ }
62
+ if (dispatch) {
63
+ tr.setNodeMarkup(selection.from, void 0, {
64
+ ...node.attrs,
65
+ latex
66
+ });
67
+ }
68
+ return true;
69
+ }
70
+ };
71
+ },
72
+ addInputRules() {
73
+ return [
74
+ // Block math with $$...$$
75
+ new InputRule({
76
+ find: /^\$\$(.+)\$\$$/,
77
+ handler: ({ state, range, match }) => {
78
+ const latex = match[1];
79
+ const { tr } = state;
80
+ tr.replaceWith(range.from, range.to, this.type.create({ latex }));
81
+ }
82
+ })
83
+ ];
84
+ }
85
+ });
86
+ var math_default = MathBlock;
87
+
88
+ export {
89
+ MathBlock,
90
+ math_default
91
+ };
@@ -0,0 +1,139 @@
1
+ "use client";
2
+ import {
3
+ TocSettings
4
+ } from "./chunk-4CDZ547I.mjs";
5
+ import {
6
+ messages
7
+ } from "./chunk-TAM3VMJT.mjs";
8
+
9
+ // src/extensions/toc/toc-view.tsx
10
+ import { ListIcon } from "@kopexa/icons";
11
+ import { toc } from "@kopexa/theme";
12
+ import { NodeViewWrapper } from "@tiptap/react";
13
+ import { useCallback, useEffect, useMemo, useState } from "react";
14
+ import { useIntl } from "react-intl";
15
+ import { jsx, jsxs } from "react/jsx-runtime";
16
+ function generateHeadingNumbers(headings, minLevel) {
17
+ const counters = [0, 0, 0, 0, 0, 0];
18
+ return headings.map((heading) => {
19
+ const levelIndex = heading.level - 1;
20
+ counters[levelIndex]++;
21
+ for (let i = levelIndex + 1; i < 6; i++) {
22
+ counters[i] = 0;
23
+ }
24
+ const numberParts = [];
25
+ for (let i = minLevel - 1; i <= levelIndex; i++) {
26
+ if (counters[i] > 0) {
27
+ numberParts.push(counters[i]);
28
+ }
29
+ }
30
+ return {
31
+ ...heading,
32
+ number: numberParts.join(".")
33
+ };
34
+ });
35
+ }
36
+ function TocNodeView({ editor, node, getPos }) {
37
+ const [headings, setHeadings] = useState([]);
38
+ const intl = useIntl();
39
+ const attrs = node.attrs;
40
+ const {
41
+ minLevel = 1,
42
+ maxLevel = 6,
43
+ numbered = false,
44
+ style = "default"
45
+ } = attrs;
46
+ const styles = useMemo(() => toc({ style }), [style]);
47
+ const updateHeadings = useCallback(() => {
48
+ const items = [];
49
+ const { doc } = editor.state;
50
+ doc.descendants((docNode, pos) => {
51
+ if (docNode.type.name === "heading") {
52
+ const level = docNode.attrs.level;
53
+ if (level >= minLevel && level <= maxLevel) {
54
+ const id = docNode.attrs.id || `heading-${pos}`;
55
+ items.push({
56
+ id,
57
+ level,
58
+ text: docNode.textContent,
59
+ pos
60
+ });
61
+ }
62
+ }
63
+ });
64
+ const numberedHeadings = generateHeadingNumbers(items, minLevel);
65
+ setHeadings(numberedHeadings);
66
+ }, [editor, minLevel, maxLevel]);
67
+ useEffect(() => {
68
+ updateHeadings();
69
+ editor.on("update", updateHeadings);
70
+ return () => {
71
+ editor.off("update", updateHeadings);
72
+ };
73
+ }, [editor, updateHeadings]);
74
+ const handleClick = useCallback(
75
+ (pos, id) => {
76
+ var _a;
77
+ editor.chain().focus().setTextSelection(pos).run();
78
+ let element = document.querySelector(`[data-id="${id}"]`);
79
+ if (!element) {
80
+ const { view } = editor;
81
+ const coords = view.coordsAtPos(pos);
82
+ const domNode = document.elementFromPoint(coords.left, coords.top);
83
+ element = (_a = domNode == null ? void 0 : domNode.closest("h1, h2, h3, h4, h5, h6")) != null ? _a : null;
84
+ }
85
+ if (element) {
86
+ element.scrollIntoView({ behavior: "smooth", block: "start" });
87
+ }
88
+ },
89
+ [editor]
90
+ );
91
+ const isEmpty = headings.length === 0;
92
+ const getIndent = (level) => {
93
+ return (level - minLevel) * 16;
94
+ };
95
+ return /* @__PURE__ */ jsxs(NodeViewWrapper, { className: styles.root(), "data-type": "toc", children: [
96
+ /* @__PURE__ */ jsxs("div", { className: styles.header(), children: [
97
+ /* @__PURE__ */ jsxs("div", { className: styles.headerContent(), children: [
98
+ /* @__PURE__ */ jsx(ListIcon, { className: styles.headerIcon() }),
99
+ /* @__PURE__ */ jsx("span", { className: styles.headerTitle(), children: intl.formatMessage(messages.title) })
100
+ ] }),
101
+ /* @__PURE__ */ jsx("div", { className: styles.headerActions(), children: /* @__PURE__ */ jsx(
102
+ TocSettings,
103
+ {
104
+ editor,
105
+ attrs,
106
+ getPos
107
+ }
108
+ ) })
109
+ ] }),
110
+ isEmpty ? /* @__PURE__ */ jsx("p", { className: styles.emptyState(), children: intl.formatMessage(messages.empty_state) }) : /* @__PURE__ */ jsx("nav", { className: styles.nav(), children: /* @__PURE__ */ jsx("ul", { className: styles.list(), children: headings.map((heading) => /* @__PURE__ */ jsxs(
111
+ "li",
112
+ {
113
+ className: styles.item(),
114
+ style: {
115
+ paddingLeft: `${getIndent(heading.level)}px`
116
+ },
117
+ children: [
118
+ numbered && heading.number && /* @__PURE__ */ jsx("span", { className: styles.itemNumber(), children: heading.number }),
119
+ /* @__PURE__ */ jsx(
120
+ "button",
121
+ {
122
+ type: "button",
123
+ onClick: () => handleClick(heading.pos, heading.id),
124
+ className: styles.itemButton(),
125
+ children: heading.text || intl.formatMessage(messages.empty_heading)
126
+ }
127
+ )
128
+ ]
129
+ },
130
+ heading.id
131
+ )) }) })
132
+ ] });
133
+ }
134
+ var toc_view_default = TocNodeView;
135
+
136
+ export {
137
+ TocNodeView,
138
+ toc_view_default
139
+ };
@@ -0,0 +1,115 @@
1
+ "use client";
2
+ import {
3
+ CalloutNodeView
4
+ } from "./chunk-VF3G2URZ.mjs";
5
+
6
+ // src/extensions/callout/index.ts
7
+ import { mergeAttributes, Node } from "@tiptap/core";
8
+ import { ReactNodeViewRenderer } from "@tiptap/react";
9
+ var CalloutNode = Node.create({
10
+ name: "calloutNode",
11
+ group: "block",
12
+ content: "block+",
13
+ defining: true,
14
+ draggable: true,
15
+ addOptions() {
16
+ return {
17
+ HTMLAttributes: {}
18
+ };
19
+ },
20
+ addAttributes() {
21
+ return {
22
+ variant: {
23
+ default: "info",
24
+ parseHTML: (element) => element.getAttribute("data-variant") || "info",
25
+ renderHTML: (attributes) => ({
26
+ "data-variant": attributes.variant
27
+ })
28
+ },
29
+ title: {
30
+ default: null,
31
+ parseHTML: (element) => element.getAttribute("data-title") || null,
32
+ renderHTML: (attributes) => {
33
+ if (!attributes.title) return {};
34
+ return {
35
+ "data-title": attributes.title
36
+ };
37
+ }
38
+ }
39
+ };
40
+ },
41
+ parseHTML() {
42
+ return [
43
+ {
44
+ tag: 'div[data-type="callout"]'
45
+ }
46
+ ];
47
+ },
48
+ renderHTML({ HTMLAttributes }) {
49
+ return [
50
+ "div",
51
+ mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
52
+ "data-type": "callout"
53
+ }),
54
+ 0
55
+ ];
56
+ },
57
+ addNodeView() {
58
+ return ReactNodeViewRenderer(CalloutNodeView);
59
+ },
60
+ addCommands() {
61
+ return {
62
+ insertCallout: (variant = "info") => ({ commands }) => {
63
+ return commands.insertContent({
64
+ type: this.name,
65
+ attrs: { variant },
66
+ content: [
67
+ {
68
+ type: "paragraph"
69
+ }
70
+ ]
71
+ });
72
+ },
73
+ updateCalloutSettings: (attrs) => ({ tr, state, dispatch }) => {
74
+ const { selection } = state;
75
+ const node = state.doc.nodeAt(selection.from);
76
+ if ((node == null ? void 0 : node.type.name) !== this.name) {
77
+ return false;
78
+ }
79
+ if (dispatch) {
80
+ tr.setNodeMarkup(selection.from, void 0, {
81
+ ...node.attrs,
82
+ ...attrs
83
+ });
84
+ }
85
+ return true;
86
+ }
87
+ };
88
+ },
89
+ addKeyboardShortcuts() {
90
+ return {
91
+ // Allow backspace at start to lift content out of callout
92
+ Backspace: ({ editor }) => {
93
+ var _a;
94
+ const { state } = editor;
95
+ const { selection } = state;
96
+ const { $from } = selection;
97
+ if ($from.parent.type.name === "paragraph" && $from.parentOffset === 0) {
98
+ const calloutNode = $from.node(-1);
99
+ if ((calloutNode == null ? void 0 : calloutNode.type.name) === this.name) {
100
+ if (calloutNode.childCount === 1 && ((_a = calloutNode.firstChild) == null ? void 0 : _a.textContent) === "") {
101
+ return editor.commands.deleteNode(this.name);
102
+ }
103
+ }
104
+ }
105
+ return false;
106
+ }
107
+ };
108
+ }
109
+ });
110
+ var callout_default = CalloutNode;
111
+
112
+ export {
113
+ CalloutNode,
114
+ callout_default
115
+ };
@@ -0,0 +1,169 @@
1
+ "use client";
2
+
3
+ // src/utils/safe-parse.ts
4
+ import DOMPurify from "dompurify";
5
+ import MarkdownIt from "markdown-it";
6
+ var md = new MarkdownIt({
7
+ html: false,
8
+ // keep it safe
9
+ linkify: true,
10
+ typographer: true
11
+ });
12
+ var DOMPURIFY_CONFIG = {
13
+ // Allowed tags (safe subset for rich text)
14
+ ALLOWED_TAGS: [
15
+ "p",
16
+ "br",
17
+ "span",
18
+ "div",
19
+ "h1",
20
+ "h2",
21
+ "h3",
22
+ "h4",
23
+ "h5",
24
+ "h6",
25
+ "strong",
26
+ "b",
27
+ "em",
28
+ "i",
29
+ "u",
30
+ "s",
31
+ "strike",
32
+ "del",
33
+ "ul",
34
+ "ol",
35
+ "li",
36
+ "blockquote",
37
+ "pre",
38
+ "code",
39
+ "a",
40
+ "img",
41
+ "table",
42
+ "thead",
43
+ "tbody",
44
+ "tr",
45
+ "th",
46
+ "td",
47
+ "hr",
48
+ "sub",
49
+ "sup",
50
+ "mark"
51
+ ],
52
+ // Allowed attributes
53
+ ALLOWED_ATTR: [
54
+ "href",
55
+ "src",
56
+ "alt",
57
+ "title",
58
+ "class",
59
+ "id",
60
+ "target",
61
+ "rel",
62
+ "colspan",
63
+ "rowspan",
64
+ "width",
65
+ "height"
66
+ ],
67
+ // Allow all data-* attributes (needed for Tiptap extensions like controlBlock)
68
+ ALLOW_DATA_ATTR: true,
69
+ // Forbid all event handlers and javascript: URLs
70
+ FORBID_ATTR: [
71
+ "onerror",
72
+ "onload",
73
+ "onclick",
74
+ "onmouseover",
75
+ "onmouseout",
76
+ "onfocus",
77
+ "onblur",
78
+ "onchange",
79
+ "onsubmit",
80
+ "onkeydown",
81
+ "onkeyup",
82
+ "onkeypress",
83
+ "ondblclick",
84
+ "oncontextmenu"
85
+ ],
86
+ // Forbid dangerous tags completely
87
+ FORBID_TAGS: [
88
+ "script",
89
+ "style",
90
+ "iframe",
91
+ "object",
92
+ "embed",
93
+ "form",
94
+ "input",
95
+ "button",
96
+ "textarea",
97
+ "select"
98
+ ],
99
+ // Block javascript: and data: URLs
100
+ ALLOWED_URI_REGEXP: /^(?:(?:https?|mailto|tel):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i
101
+ };
102
+ function sanitizeHtml(html) {
103
+ if (typeof window === "undefined") {
104
+ return html;
105
+ }
106
+ return DOMPurify.sanitize(html, DOMPURIFY_CONFIG);
107
+ }
108
+ function toPlainTextDoc(s) {
109
+ return {
110
+ type: "doc",
111
+ content: [
112
+ {
113
+ type: "paragraph",
114
+ content: s ? [{ type: "text", text: s }] : []
115
+ }
116
+ ]
117
+ };
118
+ }
119
+ function safeParseContent(content) {
120
+ if (!content || content === "") return { type: "doc", content: [] };
121
+ if (typeof content === "object") {
122
+ return content;
123
+ }
124
+ if (typeof content === "string") {
125
+ const trimmed = content.trim();
126
+ if (looksLikeJsonString(trimmed)) {
127
+ try {
128
+ const parsed = JSON.parse(trimmed);
129
+ if (isTiptapDoc(parsed)) return parsed;
130
+ } catch {
131
+ }
132
+ }
133
+ if (looksLikeHtml(trimmed)) {
134
+ return sanitizeHtml(trimmed);
135
+ }
136
+ const raw = normalizeNewlines(trimmed);
137
+ try {
138
+ const html = md.render(raw);
139
+ return sanitizeHtml(html);
140
+ } catch {
141
+ }
142
+ return toPlainTextDoc(raw);
143
+ }
144
+ return { type: "doc", content: [] };
145
+ }
146
+ function looksLikeJsonString(s) {
147
+ const c = s.trim().charCodeAt(0);
148
+ return c === 123 || c === 91;
149
+ }
150
+ function looksLikeHtml(s) {
151
+ const trimmed = s.trim();
152
+ if (trimmed.charCodeAt(0) !== 60) return false;
153
+ const secondChar = trimmed.charCodeAt(1);
154
+ return secondChar >= 65 && secondChar <= 90 || // A-Z
155
+ secondChar >= 97 && secondChar <= 122 || // a-z
156
+ secondChar === 33;
157
+ }
158
+ function isTiptapDoc(value) {
159
+ return Boolean(
160
+ value && typeof value === "object" && value.type === "doc" && Array.isArray(value.content)
161
+ );
162
+ }
163
+ function normalizeNewlines(raw) {
164
+ return raw.replace(/\r\n?/g, "\n").replace(/\\n/g, "\n");
165
+ }
166
+
167
+ export {
168
+ safeParseContent
169
+ };
@@ -0,0 +1,185 @@
1
+ "use client";
2
+ import {
3
+ messages
4
+ } from "./chunk-TAM3VMJT.mjs";
5
+
6
+ // src/extensions/toc/toc-settings.tsx
7
+ import { Button, IconButton } from "@kopexa/button";
8
+ import { Dialog } from "@kopexa/dialog";
9
+ import { SettingsIcon } from "@kopexa/icons";
10
+ import { Label } from "@kopexa/label";
11
+ import { Select } from "@kopexa/select";
12
+ import { Switch } from "@kopexa/switch";
13
+ import { useCallback, useEffect, useState } from "react";
14
+ import { useIntl } from "react-intl";
15
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
16
+ var HEADING_LEVELS = [
17
+ { value: "1", label: "H1" },
18
+ { value: "2", label: "H2" },
19
+ { value: "3", label: "H3" },
20
+ { value: "4", label: "H4" },
21
+ { value: "5", label: "H5" },
22
+ { value: "6", label: "H6" }
23
+ ];
24
+ function TocSettings({ editor, attrs, getPos }) {
25
+ const intl = useIntl();
26
+ const [isOpen, setIsOpen] = useState(false);
27
+ const [localAttrs, setLocalAttrs] = useState(attrs);
28
+ useEffect(() => {
29
+ if (isOpen) {
30
+ setLocalAttrs(attrs);
31
+ }
32
+ }, [isOpen, attrs]);
33
+ const handleSave = useCallback(() => {
34
+ const pos = getPos();
35
+ if (pos === void 0) return;
36
+ editor.view.dispatch(
37
+ editor.state.tr.setNodeMarkup(pos, void 0, localAttrs)
38
+ );
39
+ setIsOpen(false);
40
+ }, [editor, localAttrs, getPos]);
41
+ const handleCancel = useCallback(() => {
42
+ setLocalAttrs(attrs);
43
+ setIsOpen(false);
44
+ }, [attrs]);
45
+ const handleMinLevelChange = useCallback((value) => {
46
+ const minLevel = Number.parseInt(String(value), 10);
47
+ setLocalAttrs((prev) => ({
48
+ ...prev,
49
+ minLevel,
50
+ // Ensure maxLevel is not less than minLevel
51
+ maxLevel: Math.max(prev.maxLevel, minLevel)
52
+ }));
53
+ }, []);
54
+ const handleMaxLevelChange = useCallback((value) => {
55
+ const maxLevel = Number.parseInt(String(value), 10);
56
+ setLocalAttrs((prev) => ({
57
+ ...prev,
58
+ maxLevel,
59
+ // Ensure minLevel is not greater than maxLevel
60
+ minLevel: Math.min(prev.minLevel, maxLevel)
61
+ }));
62
+ }, []);
63
+ const handleNumberedChange = useCallback((checked) => {
64
+ setLocalAttrs((prev) => ({ ...prev, numbered: checked }));
65
+ }, []);
66
+ const handleStyleChange = useCallback((value) => {
67
+ setLocalAttrs((prev) => ({
68
+ ...prev,
69
+ style: String(value)
70
+ }));
71
+ }, []);
72
+ if (!editor.isEditable) {
73
+ return null;
74
+ }
75
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
76
+ /* @__PURE__ */ jsx(
77
+ IconButton,
78
+ {
79
+ size: "sm",
80
+ variant: "ghost",
81
+ "aria-label": intl.formatMessage(messages.settings),
82
+ onClick: (e) => {
83
+ e.stopPropagation();
84
+ e.preventDefault();
85
+ setIsOpen(true);
86
+ },
87
+ children: /* @__PURE__ */ jsx(SettingsIcon, { className: "size-3.5" })
88
+ }
89
+ ),
90
+ /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: setIsOpen, size: "sm", children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
91
+ /* @__PURE__ */ jsx(Dialog.Header, { children: /* @__PURE__ */ jsx(Dialog.Title, { children: intl.formatMessage(messages.settings) }) }),
92
+ /* @__PURE__ */ jsx(Dialog.Body, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
93
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
94
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
95
+ /* @__PURE__ */ jsx(Label, { className: "text-sm", children: intl.formatMessage(messages.min_level) }),
96
+ /* @__PURE__ */ jsxs(
97
+ Select,
98
+ {
99
+ size: "sm",
100
+ value: String(localAttrs.minLevel),
101
+ onValueChange: handleMinLevelChange,
102
+ children: [
103
+ /* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, {}) }),
104
+ /* @__PURE__ */ jsx(Select.Content, { children: HEADING_LEVELS.map((level) => /* @__PURE__ */ jsx(
105
+ Select.Item,
106
+ {
107
+ value: level.value,
108
+ disabled: Number.parseInt(level.value, 10) > localAttrs.maxLevel,
109
+ children: level.label
110
+ },
111
+ level.value
112
+ )) })
113
+ ]
114
+ }
115
+ )
116
+ ] }),
117
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
118
+ /* @__PURE__ */ jsx(Label, { className: "text-sm", children: intl.formatMessage(messages.max_level) }),
119
+ /* @__PURE__ */ jsxs(
120
+ Select,
121
+ {
122
+ size: "sm",
123
+ value: String(localAttrs.maxLevel),
124
+ onValueChange: handleMaxLevelChange,
125
+ children: [
126
+ /* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, {}) }),
127
+ /* @__PURE__ */ jsx(Select.Content, { children: HEADING_LEVELS.map((level) => /* @__PURE__ */ jsx(
128
+ Select.Item,
129
+ {
130
+ value: level.value,
131
+ disabled: Number.parseInt(level.value, 10) < localAttrs.minLevel,
132
+ children: level.label
133
+ },
134
+ level.value
135
+ )) })
136
+ ]
137
+ }
138
+ )
139
+ ] })
140
+ ] }),
141
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
142
+ /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
143
+ /* @__PURE__ */ jsx(Label, { className: "text-sm", children: intl.formatMessage(messages.numbered) }),
144
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: intl.formatMessage(messages.numbered_hint) })
145
+ ] }),
146
+ /* @__PURE__ */ jsx(
147
+ Switch,
148
+ {
149
+ size: "sm",
150
+ checked: localAttrs.numbered,
151
+ onCheckedChange: handleNumberedChange
152
+ }
153
+ )
154
+ ] }),
155
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
156
+ /* @__PURE__ */ jsx(Label, { className: "text-sm", children: intl.formatMessage(messages.style) }),
157
+ /* @__PURE__ */ jsxs(
158
+ Select,
159
+ {
160
+ size: "sm",
161
+ value: localAttrs.style,
162
+ onValueChange: handleStyleChange,
163
+ children: [
164
+ /* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, {}) }),
165
+ /* @__PURE__ */ jsxs(Select.Content, { children: [
166
+ /* @__PURE__ */ jsx(Select.Item, { value: "default", children: intl.formatMessage(messages.style_default) }),
167
+ /* @__PURE__ */ jsx(Select.Item, { value: "flat", children: intl.formatMessage(messages.style_flat) }),
168
+ /* @__PURE__ */ jsx(Select.Item, { value: "compact", children: intl.formatMessage(messages.style_compact) })
169
+ ] })
170
+ ]
171
+ }
172
+ )
173
+ ] })
174
+ ] }) }),
175
+ /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
176
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: handleCancel, children: intl.formatMessage(messages.cancel) }),
177
+ /* @__PURE__ */ jsx(Button, { onClick: handleSave, children: intl.formatMessage(messages.save) })
178
+ ] })
179
+ ] }) })
180
+ ] });
181
+ }
182
+
183
+ export {
184
+ TocSettings
185
+ };