@cgi-learning-hub/ui 1.14.0 → 1.15.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.
@@ -0,0 +1,2871 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_src = require("../src-BXHOgzdu.cjs");
3
+ let react = require("react");
4
+ let react_jsx_runtime = require("react/jsx-runtime");
5
+ let _mui_material_styles = require("@mui/material/styles");
6
+ let _mui_material_utils = require("@mui/material/utils");
7
+ let _tiptap_pm_state = require("@tiptap/pm/state");
8
+ let _tiptap_react = require("@tiptap/react");
9
+ require("@tiptap/pm/tables");
10
+ let _mui_material_ToggleButton = require("@mui/material/ToggleButton");
11
+ _mui_material_ToggleButton = require_src.__toESM(_mui_material_ToggleButton, 1);
12
+ let _mui_material_Tooltip = require("@mui/material/Tooltip");
13
+ _mui_material_Tooltip = require_src.__toESM(_mui_material_Tooltip, 1);
14
+ let _mui_material_Badge = require("@mui/material/Badge");
15
+ _mui_material_Badge = require_src.__toESM(_mui_material_Badge, 1);
16
+ let _mui_material_SvgIcon = require("@mui/material/SvgIcon");
17
+ let _mui_material_MenuItem = require("@mui/material/MenuItem");
18
+ _mui_material_MenuItem = require_src.__toESM(_mui_material_MenuItem, 1);
19
+ let _mui_material_Typography = require("@mui/material/Typography");
20
+ _mui_material_Typography = require_src.__toESM(_mui_material_Typography, 1);
21
+ let _mui_material_Menu = require("@mui/material/Menu");
22
+ _mui_material_Menu = require_src.__toESM(_mui_material_Menu, 1);
23
+ let _mui_material_IconButton = require("@mui/material/IconButton");
24
+ _mui_material_IconButton = require_src.__toESM(_mui_material_IconButton, 1);
25
+ let _mui_material_Stack = require("@mui/material/Stack");
26
+ _mui_material_Stack = require_src.__toESM(_mui_material_Stack, 1);
27
+ let _mui_material_InputBase = require("@mui/material/InputBase");
28
+ _mui_material_InputBase = require_src.__toESM(_mui_material_InputBase, 1);
29
+ let _mui_material_Divider = require("@mui/material/Divider");
30
+ _mui_material_Divider = require_src.__toESM(_mui_material_Divider, 1);
31
+ let _mui_material_Popover = require("@mui/material/Popover");
32
+ _mui_material_Popover = require_src.__toESM(_mui_material_Popover, 1);
33
+ //#region src/tiptap/components/icons/text-snippet-icon.tsx
34
+ var TextSnippetIcon = (0, _mui_material_utils.createSvgIcon)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M14.17,5L19,9.83V19H5V5L14.17,5L14.17,5 M14.17,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V9.83 c0-0.53-0.21-1.04-0.59-1.41l-4.83-4.83C15.21,3.21,14.7,3,14.17,3L14.17,3z M7,15h10v2H7V15z M7,11h10v2H7V11z M7,7h7v2H7V7z" }), "TextSnippet");
35
+ //#endregion
36
+ //#region src/tiptap/hooks/use-tiptap-editor.ts
37
+ function getActivePageEditor(editor) {
38
+ const pages = editor.storage.pages;
39
+ if (!pages || !("activeEditor" in pages)) return null;
40
+ return pages.activeEditor ?? null;
41
+ }
42
+ function useTiptapEditor(providedEditor) {
43
+ const { editor: coreEditor } = (0, _tiptap_react.useCurrentEditor)();
44
+ const mainEditor = providedEditor ?? coreEditor;
45
+ const [storageEditor, setStorageEditor] = (0, react.useState)(null);
46
+ (0, react.useEffect)(() => {
47
+ if (!mainEditor) {
48
+ setStorageEditor(null);
49
+ return;
50
+ }
51
+ const updateHandler = () => setStorageEditor(getActivePageEditor(mainEditor));
52
+ updateHandler();
53
+ mainEditor.on("update", updateHandler);
54
+ mainEditor.on("selectionUpdate", updateHandler);
55
+ return () => {
56
+ mainEditor.off("update", updateHandler);
57
+ mainEditor.off("selectionUpdate", updateHandler);
58
+ };
59
+ }, [mainEditor]);
60
+ (0, react.useEffect)(() => {
61
+ if (!storageEditor) return;
62
+ const handleDestroy = () => setStorageEditor(null);
63
+ storageEditor.on("destroy", handleDestroy);
64
+ return () => {
65
+ storageEditor.off("destroy", handleDestroy);
66
+ };
67
+ }, [storageEditor]);
68
+ return (0, _tiptap_react.useEditorState)({
69
+ editor: storageEditor ?? mainEditor,
70
+ selector(context) {
71
+ if (!context.editor) return {
72
+ editor: null,
73
+ editorState: void 0,
74
+ canCommand: void 0
75
+ };
76
+ return {
77
+ editor: context.editor,
78
+ editorState: context.editor.state,
79
+ canCommand: context.editor.can
80
+ };
81
+ }
82
+ }) ?? { editor: null };
83
+ }
84
+ //#endregion
85
+ //#region src/tiptap/lib/tiptap-utils.ts
86
+ var MAC_SYMBOLS = {
87
+ mod: "⌘",
88
+ command: "⌘",
89
+ meta: "⌘",
90
+ ctrl: "⌃",
91
+ control: "⌃",
92
+ alt: "⌥",
93
+ option: "⌥",
94
+ shift: "⇧",
95
+ backspace: "Del",
96
+ delete: "⌦",
97
+ enter: "⏎",
98
+ escape: "⎋",
99
+ capslock: "⇪"
100
+ };
101
+ function cn(...inputs) {
102
+ return require_src.clsx(inputs);
103
+ }
104
+ /**
105
+ * Determines if the current platform is macOS
106
+ * @returns boolean indicating if the current platform is Mac
107
+ */
108
+ function isMac() {
109
+ return typeof navigator !== "undefined" && navigator.platform.toLowerCase().includes("mac");
110
+ }
111
+ /**
112
+ * Formats a shortcut key based on the platform (Mac or non-Mac)
113
+ * @param key - The key to format (e.g., "ctrl", "alt", "shift")
114
+ * @param isMac - Boolean indicating if the platform is Mac
115
+ * @param capitalize - Whether to capitalize the key (default: true)
116
+ * @returns Formatted shortcut key symbol
117
+ */
118
+ var formatShortcutKey = (key, isMac, capitalize = true) => {
119
+ if (isMac) return MAC_SYMBOLS[key.toLowerCase()] || (capitalize ? key.toUpperCase() : key);
120
+ return capitalize ? key.charAt(0).toUpperCase() + key.slice(1) : key;
121
+ };
122
+ /**
123
+ * Parses a shortcut key string into an array of formatted key symbols
124
+ * @param shortcutKeys - The string of shortcut keys (e.g., "ctrl-alt-shift")
125
+ * @param delimiter - The delimiter used to split the keys (default: "-")
126
+ * @param capitalize - Whether to capitalize the keys (default: true)
127
+ * @returns Array of formatted shortcut key symbols
128
+ */
129
+ var parseShortcutKeys = (props) => {
130
+ const { shortcutKeys, delimiter = "+", capitalize = true } = props;
131
+ if (!shortcutKeys) return [];
132
+ return shortcutKeys.split(delimiter).map((key) => key.trim()).map((key) => formatShortcutKey(key, isMac(), capitalize));
133
+ };
134
+ /**
135
+ * Checks if a mark exists in the editor schema
136
+ * @param markName - The name of the mark to check
137
+ * @param editor - The editor instance
138
+ * @returns boolean indicating if the mark exists in the schema
139
+ */
140
+ var isMarkInSchema = (markName, editor) => {
141
+ if (!editor?.schema) return false;
142
+ return editor.schema.spec.marks.get(markName) !== void 0;
143
+ };
144
+ /**
145
+ * Checks if a node exists in the editor schema
146
+ * @param nodeName - The name of the node to check
147
+ * @param editor - The editor instance
148
+ * @returns boolean indicating if the node exists in the schema
149
+ */
150
+ var isNodeInSchema = (nodeName, editor) => {
151
+ if (!editor?.schema) return false;
152
+ return editor.schema.spec.nodes.get(nodeName) !== void 0;
153
+ };
154
+ /**
155
+ * Checks if a value is a valid number (not null, undefined, or NaN)
156
+ * @param value - The value to check
157
+ * @returns boolean indicating if the value is a valid number
158
+ */
159
+ function isValidPosition(pos) {
160
+ return typeof pos === "number" && pos >= 0;
161
+ }
162
+ /**
163
+ * Checks if one or more extensions are registered in the Tiptap editor.
164
+ * @param editor - The Tiptap editor instance
165
+ * @param extensionNames - A single extension name or an array of names to check
166
+ * @returns True if at least one of the extensions is available, false otherwise
167
+ */
168
+ var warnedMissingExtensions = /* @__PURE__ */ new Set();
169
+ function isExtensionAvailable(editor, extensionNames) {
170
+ if (!editor || editor.isDestroyed) return false;
171
+ const names = Array.isArray(extensionNames) ? extensionNames : [extensionNames];
172
+ const extensions = editor.extensionManager?.extensions;
173
+ if (!extensions) return false;
174
+ const found = names.some((name) => extensions.some((ext) => ext.name === name));
175
+ if (!found) {
176
+ const key = names.join(", ");
177
+ if (!warnedMissingExtensions.has(key)) {
178
+ warnedMissingExtensions.add(key);
179
+ console.warn(`None of the extensions [${key}] were found in the editor schema. Ensure they are included in the editor configuration.`);
180
+ }
181
+ }
182
+ return found;
183
+ }
184
+ /**
185
+ * Finds a node at the specified position with error handling
186
+ * @param editor The Tiptap editor instance
187
+ * @param position The position in the document to find the node
188
+ * @returns The node at the specified position, or null if not found
189
+ */
190
+ function findNodeAtPosition(editor, position) {
191
+ try {
192
+ const node = editor.state.doc.nodeAt(position);
193
+ if (!node) {
194
+ console.warn(`No node found at position ${position}`);
195
+ return null;
196
+ }
197
+ return node;
198
+ } catch (error) {
199
+ console.error(`Error getting node at position ${position}:`, error);
200
+ return null;
201
+ }
202
+ }
203
+ /**
204
+ * Finds the position and instance of a node in the document
205
+ * @param props Object containing editor, node (optional), and nodePos (optional)
206
+ * @param props.editor The Tiptap editor instance
207
+ * @param props.node The node to find (optional if nodePos is provided)
208
+ * @param props.nodePos The position of the node to find (optional if node is provided)
209
+ * @returns An object with the position and node, or null if not found
210
+ */
211
+ function findNodePosition(props) {
212
+ const { editor, node, nodePos } = props;
213
+ if (!editor || !editor.state?.doc) return null;
214
+ const hasValidNode = node !== void 0 && node !== null;
215
+ const hasValidPos = isValidPosition(nodePos);
216
+ if (!hasValidNode && !hasValidPos) return null;
217
+ if (hasValidNode) {
218
+ let foundPos = -1;
219
+ let foundNode = null;
220
+ editor.state.doc.descendants((currentNode, pos) => {
221
+ if (currentNode === node) {
222
+ foundPos = pos;
223
+ foundNode = currentNode;
224
+ return false;
225
+ }
226
+ return true;
227
+ });
228
+ if (foundPos !== -1 && foundNode !== null) return {
229
+ pos: foundPos,
230
+ node: foundNode
231
+ };
232
+ }
233
+ if (hasValidPos) {
234
+ const nodeAtPos = findNodeAtPosition(editor, nodePos);
235
+ if (nodeAtPos) return {
236
+ pos: nodePos,
237
+ node: nodeAtPos
238
+ };
239
+ }
240
+ return null;
241
+ }
242
+ /**
243
+ * Determines whether the current selection contains a node whose type matches
244
+ * any of the provided node type names.
245
+ * @param editor Tiptap editor instance
246
+ * @param nodeTypeNames List of node type names to match against
247
+ * @param checkAncestorNodes Whether to check ancestor node types up the depth chain
248
+ */
249
+ function isNodeTypeSelected(editor, nodeTypeNames = [], checkAncestorNodes = false) {
250
+ if (!editor || !editor.state.selection) return false;
251
+ const { selection } = editor.state;
252
+ if (selection.empty) return false;
253
+ if (selection instanceof _tiptap_pm_state.NodeSelection) {
254
+ const selectedNode = selection.node;
255
+ return selectedNode ? nodeTypeNames.includes(selectedNode.type.name) : false;
256
+ }
257
+ if (checkAncestorNodes) {
258
+ const { $from } = selection;
259
+ for (let depth = $from.depth; depth > 0; depth--) {
260
+ const ancestorNode = $from.node(depth);
261
+ if (nodeTypeNames.includes(ancestorNode.type.name)) return true;
262
+ }
263
+ }
264
+ return false;
265
+ }
266
+ /**
267
+ * Check whether the current selection is fully within nodes
268
+ * whose type names are in the provided `types` list.
269
+ *
270
+ * - NodeSelection → checks the selected node.
271
+ * - Text/AllSelection → ensures all textblocks within [from, to) are allowed.
272
+ */
273
+ function selectionWithinConvertibleTypes(editor, types = []) {
274
+ if (!editor || types.length === 0) return false;
275
+ const { state } = editor;
276
+ const { selection } = state;
277
+ const allowed = new Set(types);
278
+ if (selection instanceof _tiptap_pm_state.NodeSelection) {
279
+ const nodeType = selection.node?.type?.name;
280
+ return !!nodeType && allowed.has(nodeType);
281
+ }
282
+ if (selection instanceof _tiptap_pm_state.TextSelection || selection instanceof _tiptap_pm_state.AllSelection) {
283
+ let valid = true;
284
+ state.doc.nodesBetween(selection.from, selection.to, (node) => {
285
+ if (node.isTextblock && !allowed.has(node.type.name)) {
286
+ valid = false;
287
+ return false;
288
+ }
289
+ return valid;
290
+ });
291
+ return valid;
292
+ }
293
+ return false;
294
+ }
295
+ var ATTR_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
296
+ function isAllowedUri(uri, protocols) {
297
+ const allowedProtocols = [
298
+ "http",
299
+ "https",
300
+ "ftp",
301
+ "ftps",
302
+ "mailto",
303
+ "tel",
304
+ "callto",
305
+ "sms",
306
+ "cid",
307
+ "xmpp"
308
+ ];
309
+ if (protocols) protocols.forEach((protocol) => {
310
+ const nextProtocol = typeof protocol === "string" ? protocol : protocol.scheme;
311
+ if (nextProtocol) allowedProtocols.push(nextProtocol);
312
+ });
313
+ return !uri || uri.replace(ATTR_WHITESPACE, "").match(new RegExp(`^(?:(?:${allowedProtocols.join("|")}):|[^a-z]|[a-z0-9+.\-]+(?:[^a-z+.\-:]|$))`, "i"));
314
+ }
315
+ function sanitizeUrl(inputUrl, baseUrl, protocols) {
316
+ try {
317
+ const url = new URL(inputUrl, baseUrl);
318
+ if (isAllowedUri(url.href, protocols)) return url.href;
319
+ } catch {}
320
+ return "#";
321
+ }
322
+ function getSelectedBlockNodes(editor) {
323
+ const { doc } = editor.state;
324
+ const { from, to } = editor.state.selection;
325
+ const blocks = [];
326
+ const seen = /* @__PURE__ */ new Set();
327
+ doc.nodesBetween(from, to, (node, pos) => {
328
+ if (!node.isBlock) return;
329
+ if (!seen.has(pos)) {
330
+ seen.add(pos);
331
+ blocks.push(node);
332
+ }
333
+ return false;
334
+ });
335
+ return blocks;
336
+ }
337
+ //#endregion
338
+ //#region src/tiptap/components/ui/blockquote-button/use-blockquote.ts
339
+ var BLOCKQUOTE_SHORTCUT_KEY = "mod+shift+b";
340
+ /**
341
+ * Checks if blockquote can be toggled in the current editor state
342
+ */
343
+ function canToggleBlockquote(editor, turnInto = true) {
344
+ if (!editor || !editor.isEditable) return false;
345
+ if (!isNodeInSchema("blockquote", editor) || isNodeTypeSelected(editor, ["image"])) return false;
346
+ if (!turnInto) return editor.can().toggleWrap("blockquote");
347
+ if (!selectionWithinConvertibleTypes(editor, [
348
+ "paragraph",
349
+ "heading",
350
+ "bulletList",
351
+ "orderedList",
352
+ "taskList",
353
+ "blockquote",
354
+ "codeBlock"
355
+ ])) return false;
356
+ return editor.can().toggleWrap("blockquote") || editor.can().clearNodes();
357
+ }
358
+ /**
359
+ * Toggles blockquote formatting for a specific node or the current selection
360
+ */
361
+ function toggleBlockquote(editor) {
362
+ if (!editor || !editor.isEditable) return false;
363
+ if (!canToggleBlockquote(editor)) return false;
364
+ try {
365
+ const view = editor.view;
366
+ let state = view.state;
367
+ let tr = state.tr;
368
+ const blocks = getSelectedBlockNodes(editor);
369
+ const isPossibleToTurnInto = selectionWithinConvertibleTypes(editor, [
370
+ "paragraph",
371
+ "heading",
372
+ "bulletList",
373
+ "orderedList",
374
+ "taskList",
375
+ "blockquote",
376
+ "codeBlock"
377
+ ]) && blocks.length === 1;
378
+ if ((state.selection.empty || state.selection instanceof _tiptap_pm_state.TextSelection) && isPossibleToTurnInto) {
379
+ const pos = findNodePosition({
380
+ editor,
381
+ node: state.selection.$anchor.node(1)
382
+ })?.pos;
383
+ if (!isValidPosition(pos)) return false;
384
+ tr = tr.setSelection(_tiptap_pm_state.NodeSelection.create(state.doc, pos));
385
+ view.dispatch(tr);
386
+ state = view.state;
387
+ }
388
+ const selection = state.selection;
389
+ let chain = editor.chain().focus();
390
+ if (selection instanceof _tiptap_pm_state.NodeSelection) {
391
+ const firstChild = selection.node.firstChild?.firstChild;
392
+ const lastChild = selection.node.lastChild?.lastChild;
393
+ const from = firstChild ? selection.from + firstChild.nodeSize : selection.from + 1;
394
+ const to = lastChild ? selection.to - lastChild.nodeSize : selection.to - 1;
395
+ const resolvedFrom = state.doc.resolve(from);
396
+ const resolvedTo = state.doc.resolve(to);
397
+ chain = chain.setTextSelection(_tiptap_pm_state.TextSelection.between(resolvedFrom, resolvedTo)).clearNodes();
398
+ }
399
+ (editor.isActive("blockquote") ? chain.lift("blockquote") : chain.wrapIn("blockquote")).run();
400
+ editor.chain().focus().selectTextblockEnd().run();
401
+ return true;
402
+ } catch {
403
+ return false;
404
+ }
405
+ }
406
+ /**
407
+ * Determines if the blockquote button should be shown
408
+ */
409
+ function shouldShowButton$5(props) {
410
+ const { editor, hideWhenUnavailable } = props;
411
+ if (!editor) return false;
412
+ if (!hideWhenUnavailable) return true;
413
+ if (!editor.isEditable) return false;
414
+ if (!isNodeInSchema("blockquote", editor)) return false;
415
+ if (!editor.isActive("code")) return canToggleBlockquote(editor);
416
+ return true;
417
+ }
418
+ /**
419
+ * Custom hook that provides blockquote functionality for Tiptap editor
420
+ *
421
+ * @example
422
+ * ```tsx
423
+ * // Simple usage - no params needed
424
+ * function MySimpleBlockquoteButton() {
425
+ * const { isVisible, handleToggle, isActive } = useBlockquote()
426
+ *
427
+ * if (!isVisible) return null
428
+ *
429
+ * return <button onClick={handleToggle}>Blockquote</button>
430
+ * }
431
+ *
432
+ * // Advanced usage with configuration
433
+ * function MyAdvancedBlockquoteButton() {
434
+ * const { isVisible, handleToggle, label, isActive } = useBlockquote({
435
+ * editor: myEditor,
436
+ * hideWhenUnavailable: true,
437
+ * onToggled: () => console.log('Blockquote toggled!')
438
+ * })
439
+ *
440
+ * if (!isVisible) return null
441
+ *
442
+ * return (
443
+ * <MyButton
444
+ * onClick={handleToggle}
445
+ * aria-label={label}
446
+ * aria-pressed={isActive}
447
+ * >
448
+ * Toggle Blockquote
449
+ * </MyButton>
450
+ * )
451
+ * }
452
+ * ```
453
+ */
454
+ function useBlockquote(config) {
455
+ const { editor: providedEditor, hideWhenUnavailable = false, onToggled } = config || {};
456
+ const { editor } = useTiptapEditor(providedEditor);
457
+ const [isVisible, setIsVisible] = (0, react.useState)(true);
458
+ const canToggle = canToggleBlockquote(editor);
459
+ const isActive = editor?.isActive("blockquote") || false;
460
+ (0, react.useEffect)(() => {
461
+ if (!editor) return;
462
+ const handleSelectionUpdate = () => {
463
+ setIsVisible(shouldShowButton$5({
464
+ editor,
465
+ hideWhenUnavailable
466
+ }));
467
+ };
468
+ handleSelectionUpdate();
469
+ editor.on("selectionUpdate", handleSelectionUpdate);
470
+ return () => {
471
+ editor.off("selectionUpdate", handleSelectionUpdate);
472
+ };
473
+ }, [editor, hideWhenUnavailable]);
474
+ return {
475
+ isVisible,
476
+ isActive,
477
+ handleToggle: (0, react.useCallback)(() => {
478
+ if (!editor) return false;
479
+ const success = toggleBlockquote(editor);
480
+ if (success) onToggled?.();
481
+ return success;
482
+ }, [editor, onToggled]),
483
+ canToggle,
484
+ label: "Citation en bloc",
485
+ shortcutKeys: BLOCKQUOTE_SHORTCUT_KEY,
486
+ Icon: TextSnippetIcon
487
+ };
488
+ }
489
+ //#endregion
490
+ //#region src/tiptap/components/ui-primitive/button/button.tsx
491
+ var ShortcutDisplay = ({ shortcuts }) => {
492
+ if (shortcuts.length === 0) return null;
493
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: shortcuts.map((key, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [index > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("kbd", { children: "+" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("kbd", { children: key })] }, index)) });
494
+ };
495
+ var Button = (0, react.forwardRef)(({ className, children, tooltip, showTooltip = true, shortcutKeys, ...props }, ref) => {
496
+ const shortcuts = (0, react.useMemo)(() => parseShortcutKeys({ shortcutKeys }), [shortcutKeys]);
497
+ if (!tooltip || !showTooltip) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material_ToggleButton.default, {
498
+ size: "small",
499
+ color: "primary",
500
+ ref,
501
+ sx: {
502
+ border: "none",
503
+ "&:disabled": { border: "none" }
504
+ },
505
+ ...props,
506
+ children
507
+ });
508
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material_Tooltip.default, {
509
+ enterDelay: 200,
510
+ title: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [tooltip, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShortcutDisplay, { shortcuts })] }),
511
+ slotProps: { popper: { modifiers: [{
512
+ name: "offset",
513
+ options: { offset: [0, -8] }
514
+ }] } },
515
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material_ToggleButton.default, {
516
+ size: "small",
517
+ color: "primary",
518
+ ref,
519
+ sx: {
520
+ border: "none",
521
+ "&:disabled": { border: "none" }
522
+ },
523
+ ...props,
524
+ children
525
+ })
526
+ });
527
+ });
528
+ Button.displayName = "Button";
529
+ //#endregion
530
+ //#region src/tiptap/components/ui/blockquote-button/blockquote-button.tsx
531
+ function BlockquoteShortcutBadge({ shortcutKeys = BLOCKQUOTE_SHORTCUT_KEY }) {
532
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material_Badge.default, { children: parseShortcutKeys({ shortcutKeys }) });
533
+ }
534
+ /**
535
+ * Button component for toggling blockquote in a Tiptap editor.
536
+ *
537
+ * For custom button implementations, use the `useBlockquote` hook instead.
538
+ */
539
+ var BlockquoteButton = (0, react.forwardRef)(({ editor: providedEditor, text, hideWhenUnavailable = false, onToggled, showShortcut = false, onClick, children, ...buttonProps }, ref) => {
540
+ const { editor } = useTiptapEditor(providedEditor);
541
+ const { isVisible, canToggle, isActive, handleToggle, label, shortcutKeys, Icon } = useBlockquote({
542
+ editor,
543
+ hideWhenUnavailable,
544
+ onToggled
545
+ });
546
+ const handleClick = (0, react.useCallback)((event) => {
547
+ onClick?.(event, !isActive);
548
+ if (event.defaultPrevented) return;
549
+ handleToggle();
550
+ }, [handleToggle, onClick]);
551
+ if (!isVisible) return null;
552
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
553
+ value: "blockquote",
554
+ type: "button",
555
+ selected: isActive,
556
+ role: "button",
557
+ tabIndex: -1,
558
+ disabled: !canToggle,
559
+ "data-disabled": !canToggle,
560
+ "aria-label": label,
561
+ tooltip: label,
562
+ onClick: handleClick,
563
+ ...buttonProps,
564
+ ref,
565
+ children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
566
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { fontSize: "small" }),
567
+ text && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
568
+ className: "tiptap-button-text",
569
+ children: text
570
+ }),
571
+ showShortcut && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockquoteShortcutBadge, { shortcutKeys })
572
+ ] })
573
+ });
574
+ });
575
+ BlockquoteButton.displayName = "BlockquoteButton";
576
+ //#endregion
577
+ //#region src/tiptap/components/icons/integration-instructions-icon.tsx
578
+ var IntegrationInstructionIcon = (0, _mui_material_utils.createSvgIcon)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", { children: [
579
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
580
+ fill: "none",
581
+ height: "24",
582
+ width: "24"
583
+ }),
584
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
585
+ cx: "12",
586
+ cy: "3.5",
587
+ fill: "none",
588
+ r: ".75"
589
+ }),
590
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
591
+ cx: "12",
592
+ cy: "3.5",
593
+ fill: "none",
594
+ r: ".75"
595
+ }),
596
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polygon", {
597
+ fill: "none",
598
+ points: "5,15 5,16 5,19 19,19 19,16 19,15 19,5 5,5"
599
+ }),
600
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", { children: [
601
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polygon", { points: "11,14.17 8.83,12 11,9.83 9.59,8.41 6,12 9.59,15.59" }),
602
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polygon", { points: "14.41,15.59 18,12 14.41,8.41 13,9.83 15.17,12 13,14.17" }),
603
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M19,3h-4.18C14.4,1.84,13.3,1,12,1S9.6,1.84,9.18,3H5C4.86,3,4.73,3.01,4.6,3.04C4.21,3.12,3.86,3.32,3.59,3.59 c-0.18,0.18-0.33,0.4-0.43,0.64C3.06,4.46,3,4.72,3,5v10v1v3c0,0.27,0.06,0.54,0.16,0.78c0.1,0.24,0.25,0.45,0.43,0.64 c0.27,0.27,0.62,0.47,1.01,0.55C4.73,20.99,4.86,21,5,21h14c1.1,0,2-0.9,2-2v-3v-1V5C21,3.9,20.1,3,19,3z M12,2.75 c0.41,0,0.75,0.34,0.75,0.75S12.41,4.25,12,4.25s-0.75-0.34-0.75-0.75S11.59,2.75,12,2.75z M19,15v1v3H5v-3v-1V5h14V15z" })
604
+ ] })
605
+ ] }), "IntegrationInstruction");
606
+ //#endregion
607
+ //#region src/tiptap/components/ui/code-block-button/use-code-block.ts
608
+ var CODE_BLOCK_SHORTCUT_KEY = "mod+alt+c";
609
+ /**
610
+ * Checks if code block can be toggled in the current editor state
611
+ */
612
+ function canToggle$1(editor, turnInto = true) {
613
+ if (!editor || !editor.isEditable) return false;
614
+ if (!isNodeInSchema("codeBlock", editor) || isNodeTypeSelected(editor, ["image"])) return false;
615
+ if (!turnInto) return editor.can().toggleNode("codeBlock", "paragraph");
616
+ if (!selectionWithinConvertibleTypes(editor, [
617
+ "paragraph",
618
+ "heading",
619
+ "bulletList",
620
+ "orderedList",
621
+ "taskList",
622
+ "blockquote",
623
+ "codeBlock"
624
+ ])) return false;
625
+ return editor.can().toggleNode("codeBlock", "paragraph") || editor.can().clearNodes();
626
+ }
627
+ /**
628
+ * Toggles code block in the editor
629
+ */
630
+ function toggleCodeBlock(editor) {
631
+ if (!editor || !editor.isEditable) return false;
632
+ if (!canToggle$1(editor)) return false;
633
+ try {
634
+ const view = editor.view;
635
+ let state = view.state;
636
+ let tr = state.tr;
637
+ const blocks = getSelectedBlockNodes(editor);
638
+ const isPossibleToTurnInto = selectionWithinConvertibleTypes(editor, [
639
+ "paragraph",
640
+ "heading",
641
+ "bulletList",
642
+ "orderedList",
643
+ "taskList",
644
+ "blockquote",
645
+ "codeBlock"
646
+ ]) && blocks.length === 1;
647
+ if ((state.selection.empty || state.selection instanceof _tiptap_pm_state.TextSelection) && isPossibleToTurnInto) {
648
+ const pos = findNodePosition({
649
+ editor,
650
+ node: state.selection.$anchor.node(1)
651
+ })?.pos;
652
+ if (!isValidPosition(pos)) return false;
653
+ tr = tr.setSelection(_tiptap_pm_state.NodeSelection.create(state.doc, pos));
654
+ view.dispatch(tr);
655
+ state = view.state;
656
+ }
657
+ const selection = state.selection;
658
+ let chain = editor.chain().focus();
659
+ if (selection instanceof _tiptap_pm_state.NodeSelection) {
660
+ const firstChild = selection.node.firstChild?.firstChild;
661
+ const lastChild = selection.node.lastChild?.lastChild;
662
+ const from = firstChild ? selection.from + firstChild.nodeSize : selection.from + 1;
663
+ const to = lastChild ? selection.to - lastChild.nodeSize : selection.to - 1;
664
+ const resolvedFrom = state.doc.resolve(from);
665
+ const resolvedTo = state.doc.resolve(to);
666
+ chain = chain.setTextSelection(_tiptap_pm_state.TextSelection.between(resolvedFrom, resolvedTo)).clearNodes();
667
+ }
668
+ (editor.isActive("codeBlock") ? chain.setNode("paragraph") : chain.toggleNode("codeBlock", "paragraph")).run();
669
+ editor.chain().focus().selectTextblockEnd().run();
670
+ return true;
671
+ } catch {
672
+ return false;
673
+ }
674
+ }
675
+ /**
676
+ * Determines if the code block button should be shown
677
+ */
678
+ function shouldShowButton$4(props) {
679
+ const { editor, hideWhenUnavailable } = props;
680
+ if (!editor) return false;
681
+ if (!hideWhenUnavailable) return true;
682
+ if (!editor.isEditable) return false;
683
+ if (!isNodeInSchema("codeBlock", editor)) return false;
684
+ if (!editor.isActive("code")) return canToggle$1(editor);
685
+ return true;
686
+ }
687
+ /**
688
+ * Custom hook that provides code block functionality for Tiptap editor
689
+ *
690
+ * @example
691
+ * ```tsx
692
+ * // Simple usage - no params needed
693
+ * function MySimpleCodeBlockButton() {
694
+ * const { isVisible, isActive, handleToggle } = useCodeBlock()
695
+ *
696
+ * if (!isVisible) return null
697
+ *
698
+ * return (
699
+ * <button
700
+ * onClick={handleToggle}
701
+ * aria-pressed={isActive}
702
+ * >
703
+ * Code Block
704
+ * </button>
705
+ * )
706
+ * }
707
+ *
708
+ * // Advanced usage with configuration
709
+ * function MyAdvancedCodeBlockButton() {
710
+ * const { isVisible, isActive, handleToggle, label } = useCodeBlock({
711
+ * editor: myEditor,
712
+ * hideWhenUnavailable: true,
713
+ * onToggled: (isActive) => console.log('Code block toggled:', isActive)
714
+ * })
715
+ *
716
+ * if (!isVisible) return null
717
+ *
718
+ * return (
719
+ * <MyButton
720
+ * onClick={handleToggle}
721
+ * aria-label={label}
722
+ * aria-pressed={isActive}
723
+ * >
724
+ * Toggle Code Block
725
+ * </MyButton>
726
+ * )
727
+ * }
728
+ * ```
729
+ */
730
+ function useCodeBlock(config) {
731
+ const { editor: providedEditor, hideWhenUnavailable = false, onToggled } = config || {};
732
+ const { editor } = useTiptapEditor(providedEditor);
733
+ const [isVisible, setIsVisible] = (0, react.useState)(true);
734
+ const canToggleState = canToggle$1(editor);
735
+ const isActive = editor?.isActive("codeBlock") || false;
736
+ (0, react.useEffect)(() => {
737
+ if (!editor) return;
738
+ const handleSelectionUpdate = () => {
739
+ setIsVisible(shouldShowButton$4({
740
+ editor,
741
+ hideWhenUnavailable
742
+ }));
743
+ };
744
+ handleSelectionUpdate();
745
+ editor.on("selectionUpdate", handleSelectionUpdate);
746
+ return () => {
747
+ editor.off("selectionUpdate", handleSelectionUpdate);
748
+ };
749
+ }, [editor, hideWhenUnavailable]);
750
+ return {
751
+ isVisible,
752
+ isActive,
753
+ handleToggle: (0, react.useCallback)(() => {
754
+ if (!editor) return false;
755
+ const success = toggleCodeBlock(editor);
756
+ if (success) onToggled?.();
757
+ return success;
758
+ }, [editor, onToggled]),
759
+ canToggle: canToggleState,
760
+ label: "Bloc de code",
761
+ shortcutKeys: CODE_BLOCK_SHORTCUT_KEY,
762
+ Icon: IntegrationInstructionIcon
763
+ };
764
+ }
765
+ //#endregion
766
+ //#region src/tiptap/components/ui/code-block-button/code-block-button.tsx
767
+ function CodeBlockShortcutBadge({ shortcutKeys = CODE_BLOCK_SHORTCUT_KEY }) {
768
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material_Badge.default, { children: parseShortcutKeys({ shortcutKeys }) });
769
+ }
770
+ /**
771
+ * Button component for toggling code block in a Tiptap editor.
772
+ *
773
+ * For custom button implementations, use the `useCodeBlock` hook instead.
774
+ */
775
+ var CodeBlockButton = (0, react.forwardRef)(({ editor: providedEditor, text, hideWhenUnavailable = false, onToggled, showShortcut = false, onClick, children, ...buttonProps }, ref) => {
776
+ const { editor } = useTiptapEditor(providedEditor);
777
+ const { isVisible, canToggle, isActive, handleToggle, label, shortcutKeys, Icon } = useCodeBlock({
778
+ editor,
779
+ hideWhenUnavailable,
780
+ onToggled
781
+ });
782
+ const handleClick = (0, react.useCallback)((event) => {
783
+ onClick?.(event, !isActive);
784
+ if (event.defaultPrevented) return;
785
+ handleToggle();
786
+ }, [handleToggle, onClick]);
787
+ if (!isVisible) return null;
788
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
789
+ value: "codeBlock",
790
+ type: "button",
791
+ selected: isActive,
792
+ role: "button",
793
+ disabled: !canToggle,
794
+ "data-disabled": !canToggle,
795
+ tabIndex: -1,
796
+ "aria-label": label,
797
+ tooltip: label,
798
+ onClick: handleClick,
799
+ ...buttonProps,
800
+ ref,
801
+ children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
802
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { fontSize: "small" }),
803
+ text && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
804
+ className: "tiptap-button-text",
805
+ children: text
806
+ }),
807
+ showShortcut && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodeBlockShortcutBadge, { shortcutKeys })
808
+ ] })
809
+ });
810
+ });
811
+ CodeBlockButton.displayName = "CodeBlockButton";
812
+ //#endregion
813
+ //#region ../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.2._02cb1dd638d2ccdea7d16c50ea4ea174/node_modules/@mui/icons-material/ExpandMoreRounded.mjs
814
+ var ExpandMoreRounded_default = (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M15.88 9.29 12 13.17 8.12 9.29a.996.996 0 0 0-1.41 0c-.39.39-.39 1.02 0 1.41l4.59 4.59c.39.39 1.02.39 1.41 0l4.59-4.59c.39-.39.39-1.02 0-1.41-.39-.38-1.03-.39-1.42 0" }), "ExpandMoreRounded");
815
+ //#endregion
816
+ //#region src/tiptap/components/icons/heading-five-icon.tsx
817
+ var HeadingFiveIcon = (0, _mui_material_utils.createSvgIcon)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
818
+ d: "M5 6C5 5.44772 4.55228 5 4 5C3.44772 5 3 5.44772 3 6V18C3 18.5523 3.44772 19 4 19C4.55228 19 5 18.5523 5 18V13H11V18C11 18.5523 11.4477 19 12 19C12.5523 19 13 18.5523 13 18V6C13 5.44772 12.5523 5 12 5C11.4477 5 11 5.44772 11 6V11H5V6Z",
819
+ fill: "currentColor"
820
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
821
+ d: "M16 10C16 9.44772 16.4477 9 17 9H21C21.5523 9 22 9.44772 22 10C22 10.5523 21.5523 11 21 11H18V12H18.3C20.2754 12 22 13.4739 22 15.5C22 17.5261 20.2754 19 18.3 19C17.6457 19 17.0925 18.8643 16.5528 18.5944C16.0588 18.3474 15.8586 17.7468 16.1055 17.2528C16.3525 16.7588 16.9532 16.5586 17.4472 16.8056C17.7074 16.9357 17.9542 17 18.3 17C19.3246 17 20 16.2739 20 15.5C20 14.7261 19.3246 14 18.3 14H17C16.4477 14 16 13.5523 16 13L16 12.9928V10Z",
822
+ fill: "currentColor"
823
+ })] }), "HeadingFiveIcon");
824
+ //#endregion
825
+ //#region src/tiptap/components/icons/heading-four-icon.tsx
826
+ var HeadingFourIcon = (0, _mui_material_utils.createSvgIcon)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
827
+ d: "M4 5C4.55228 5 5 5.44772 5 6V11H11V6C11 5.44772 11.4477 5 12 5C12.5523 5 13 5.44772 13 6V18C13 18.5523 12.5523 19 12 19C11.4477 19 11 18.5523 11 18V13H5V18C5 18.5523 4.55228 19 4 19C3.44772 19 3 18.5523 3 18V6C3 5.44772 3.44772 5 4 5Z",
828
+ fill: "currentColor"
829
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
830
+ d: "M17 9C17.5523 9 18 9.44772 18 10V13H20V10C20 9.44772 20.4477 9 21 9C21.5523 9 22 9.44772 22 10V18C22 18.5523 21.5523 19 21 19C20.4477 19 20 18.5523 20 18V15H17C16.4477 15 16 14.5523 16 14V10C16 9.44772 16.4477 9 17 9Z",
831
+ fill: "currentColor"
832
+ })] }), "HeadingFour");
833
+ //#endregion
834
+ //#region src/tiptap/components/icons/heading-one-icon.tsx
835
+ var HeadingOneIcon = (0, _mui_material_utils.createSvgIcon)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
836
+ d: "M5 6C5 5.44772 4.55228 5 4 5C3.44772 5 3 5.44772 3 6V18C3 18.5523 3.44772 19 4 19C4.55228 19 5 18.5523 5 18V13H11V18C11 18.5523 11.4477 19 12 19C12.5523 19 13 18.5523 13 18V6C13 5.44772 12.5523 5 12 5C11.4477 5 11 5.44772 11 6V11H5V6Z",
837
+ fill: "currentColor"
838
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
839
+ d: "M21.0001 10C21.0001 9.63121 20.7971 9.29235 20.472 9.11833C20.1468 8.94431 19.7523 8.96338 19.4454 9.16795L16.4454 11.168C15.9859 11.4743 15.8617 12.0952 16.1681 12.5547C16.4744 13.0142 17.0953 13.1384 17.5548 12.8321L19.0001 11.8685V18C19.0001 18.5523 19.4478 19 20.0001 19C20.5524 19 21.0001 18.5523 21.0001 18V10Z",
840
+ fill: "currentColor"
841
+ })] }), "HeadingOne");
842
+ //#endregion
843
+ //#region src/tiptap/components/icons/heading-six-icon.tsx
844
+ var HeadingSixIcon = (0, _mui_material_utils.createSvgIcon)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
845
+ d: "M5 6C5 5.44772 4.55228 5 4 5C3.44772 5 3 5.44772 3 6V18C3 18.5523 3.44772 19 4 19C4.55228 19 5 18.5523 5 18V13H11V18C11 18.5523 11.4477 19 12 19C12.5523 19 13 18.5523 13 18V6C13 5.44772 12.5523 5 12 5C11.4477 5 11 5.44772 11 6V11H5V6Z",
846
+ fill: "currentColor"
847
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
848
+ fillRule: "evenodd",
849
+ clipRule: "evenodd",
850
+ d: "M20.7071 9.29289C21.0976 9.68342 21.0976 10.3166 20.7071 10.7071C19.8392 11.575 19.2179 12.2949 18.7889 13.0073C18.8587 13.0025 18.929 13 19 13C20.6569 13 22 14.3431 22 16C22 17.6569 20.6569 19 19 19C17.3431 19 16 17.6569 16 16C16 14.6007 16.2837 13.4368 16.8676 12.3419C17.4384 11.2717 18.2728 10.3129 19.2929 9.29289C19.6834 8.90237 20.3166 8.90237 20.7071 9.29289ZM19 17C18.4477 17 18 16.5523 18 16C18 15.4477 18.4477 15 19 15C19.5523 15 20 15.4477 20 16C20 16.5523 19.5523 17 19 17Z",
851
+ fill: "currentColor"
852
+ })] }), "HeadingSix");
853
+ //#endregion
854
+ //#region src/tiptap/components/icons/heading-three-icon.tsx
855
+ var HeadingThreeIcon = (0, _mui_material_utils.createSvgIcon)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", { children: [
856
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
857
+ d: "M4 5C4.55228 5 5 5.44772 5 6V11H11V6C11 5.44772 11.4477 5 12 5C12.5523 5 13 5.44772 13 6V18C13 18.5523 12.5523 19 12 19C11.4477 19 11 18.5523 11 18V13H5V18C5 18.5523 4.55228 19 4 19C3.44772 19 3 18.5523 3 18V6C3 5.44772 3.44772 5 4 5Z",
858
+ fill: "currentColor"
859
+ }),
860
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
861
+ fillRule: "evenodd",
862
+ clipRule: "evenodd",
863
+ d: "M19.4608 11.2169C19.1135 11.0531 18.5876 11.0204 18.0069 11.3619C17.5309 11.642 16.918 11.4831 16.638 11.007C16.358 10.531 16.5169 9.91809 16.9929 9.63807C18.1123 8.97962 19.3364 8.94691 20.314 9.40808C21.2839 9.86558 21.9999 10.818 21.9999 12C21.9999 12.7957 21.6838 13.5587 21.1212 14.1213C20.5586 14.6839 19.7956 15 18.9999 15C18.4476 15 17.9999 14.5523 17.9999 14C17.9999 13.4477 18.4476 13 18.9999 13C19.2651 13 19.5195 12.8947 19.707 12.7071C19.8946 12.5196 19.9999 12.2652 19.9999 12C19.9999 11.6821 19.8159 11.3844 19.4608 11.2169Z",
864
+ fill: "currentColor"
865
+ }),
866
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
867
+ fillRule: "evenodd",
868
+ clipRule: "evenodd",
869
+ d: "M18.0001 14C18.0001 13.4477 18.4478 13 19.0001 13C19.7957 13 20.5588 13.3161 21.1214 13.8787C21.684 14.4413 22.0001 15.2043 22.0001 16C22.0001 17.2853 21.2767 18.3971 20.1604 18.8994C19.0257 19.41 17.642 19.2315 16.4001 18.3C15.9582 17.9686 15.8687 17.3418 16.2001 16.9C16.5314 16.4582 17.1582 16.3686 17.6001 16.7C18.3581 17.2685 18.9744 17.24 19.3397 17.0756C19.7234 16.9029 20.0001 16.5147 20.0001 16C20.0001 15.7348 19.8947 15.4804 19.7072 15.2929C19.5196 15.1054 19.2653 15 19.0001 15C18.4478 15 18.0001 14.5523 18.0001 14Z",
870
+ fill: "currentColor"
871
+ })
872
+ ] }), "HeadingThree");
873
+ //#endregion
874
+ //#region src/tiptap/components/ui/heading-menu-item/use-heading.ts
875
+ var headingIcons = {
876
+ 1: HeadingOneIcon,
877
+ 2: (0, _mui_material_utils.createSvgIcon)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
878
+ d: "M5 6C5 5.44772 4.55228 5 4 5C3.44772 5 3 5.44772 3 6V18C3 18.5523 3.44772 19 4 19C4.55228 19 5 18.5523 5 18V13H11V18C11 18.5523 11.4477 19 12 19C12.5523 19 13 18.5523 13 18V6C13 5.44772 12.5523 5 12 5C11.4477 5 11 5.44772 11 6V11H5V6Z",
879
+ fill: "currentColor"
880
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
881
+ d: "M22.0001 12C22.0001 10.7611 21.1663 9.79297 20.0663 9.42632C18.9547 9.05578 17.6171 9.28724 16.4001 10.2C15.9582 10.5314 15.8687 11.1582 16.2001 11.6C16.5314 12.0418 17.1582 12.1314 17.6001 11.8C18.383 11.2128 19.0455 11.1942 19.4338 11.3237C19.8339 11.457 20.0001 11.7389 20.0001 12C20.0001 12.4839 19.8554 12.7379 19.6537 12.9481C19.4275 13.1837 19.1378 13.363 18.7055 13.6307C18.6313 13.6767 18.553 13.7252 18.4701 13.777C17.9572 14.0975 17.3128 14.5261 16.8163 15.2087C16.3007 15.9177 16.0001 16.8183 16.0001 18C16.0001 18.5523 16.4478 19 17.0001 19H21.0001C21.5523 19 22.0001 18.5523 22.0001 18C22.0001 17.4477 21.5523 17 21.0001 17H18.131C18.21 16.742 18.3176 16.5448 18.4338 16.385C18.6873 16.0364 19.0429 15.7775 19.5301 15.473C19.5898 15.4357 19.6536 15.3966 19.7205 15.3556C20.139 15.0992 20.6783 14.7687 21.0964 14.3332C21.6447 13.7621 22.0001 13.0161 22.0001 12Z",
882
+ fill: "currentColor"
883
+ })] }), "HeadingTwo"),
884
+ 3: HeadingThreeIcon,
885
+ 4: HeadingFourIcon,
886
+ 5: HeadingFiveIcon,
887
+ 6: HeadingSixIcon
888
+ };
889
+ var HEADING_SHORTCUT_KEYS = {
890
+ 1: "ctrl+alt+1",
891
+ 2: "ctrl+alt+2",
892
+ 3: "ctrl+alt+3",
893
+ 4: "ctrl+alt+4",
894
+ 5: "ctrl+alt+5",
895
+ 6: "ctrl+alt+6"
896
+ };
897
+ /**
898
+ * Checks if heading can be toggled in the current editor state
899
+ */
900
+ function canToggle(editor, level, turnInto = true) {
901
+ if (!editor || !editor.isEditable) return false;
902
+ if (!isNodeInSchema("heading", editor) || isNodeTypeSelected(editor, ["image"])) return false;
903
+ if (!turnInto) return level ? editor.can().setNode("heading", { level }) : editor.can().setNode("heading");
904
+ if (!selectionWithinConvertibleTypes(editor, [
905
+ "paragraph",
906
+ "heading",
907
+ "bulletList",
908
+ "orderedList",
909
+ "taskList",
910
+ "blockquote",
911
+ "codeBlock"
912
+ ])) return false;
913
+ return level ? editor.can().setNode("heading", { level }) || editor.can().clearNodes() : editor.can().setNode("heading") || editor.can().clearNodes();
914
+ }
915
+ /**
916
+ * Checks if heading is currently active
917
+ */
918
+ function isHeadingActive(editor, level) {
919
+ if (!editor || !editor.isEditable) return false;
920
+ if (Array.isArray(level)) return level.some((l) => editor.isActive("heading", { level: l }));
921
+ return level ? editor.isActive("heading", { level }) : editor.isActive("heading");
922
+ }
923
+ /**
924
+ * Toggles heading in the editor
925
+ */
926
+ function toggleHeading(editor, level) {
927
+ if (!editor || !editor.isEditable) return false;
928
+ const levels = Array.isArray(level) ? level : [level];
929
+ const toggleLevel = levels.find((l) => canToggle(editor, l));
930
+ if (!toggleLevel) return false;
931
+ try {
932
+ const view = editor.view;
933
+ let state = view.state;
934
+ let tr = state.tr;
935
+ const blocks = getSelectedBlockNodes(editor);
936
+ const isPossibleToTurnInto = selectionWithinConvertibleTypes(editor, [
937
+ "paragraph",
938
+ "heading",
939
+ "bulletList",
940
+ "orderedList",
941
+ "taskList",
942
+ "blockquote",
943
+ "codeBlock"
944
+ ]) && blocks.length === 1;
945
+ if ((state.selection.empty || state.selection instanceof _tiptap_pm_state.TextSelection) && isPossibleToTurnInto) {
946
+ const pos = findNodePosition({
947
+ editor,
948
+ node: state.selection.$anchor.node(1)
949
+ })?.pos;
950
+ if (!isValidPosition(pos)) return false;
951
+ tr = tr.setSelection(_tiptap_pm_state.NodeSelection.create(state.doc, pos));
952
+ view.dispatch(tr);
953
+ state = view.state;
954
+ }
955
+ const selection = state.selection;
956
+ let chain = editor.chain().focus();
957
+ if (selection instanceof _tiptap_pm_state.NodeSelection) {
958
+ const firstChild = selection.node.firstChild?.firstChild;
959
+ const lastChild = selection.node.lastChild?.lastChild;
960
+ const from = firstChild ? selection.from + firstChild.nodeSize : selection.from + 1;
961
+ const to = lastChild ? selection.to - lastChild.nodeSize : selection.to - 1;
962
+ const resolvedFrom = state.doc.resolve(from);
963
+ const resolvedTo = state.doc.resolve(to);
964
+ chain = chain.setTextSelection(_tiptap_pm_state.TextSelection.between(resolvedFrom, resolvedTo)).clearNodes();
965
+ }
966
+ (levels.some((l) => editor.isActive("heading", { level: l })) ? chain.setNode("paragraph") : chain.setNode("heading", { level: toggleLevel })).run();
967
+ editor.chain().focus().selectTextblockEnd().run();
968
+ return true;
969
+ } catch {
970
+ return false;
971
+ }
972
+ }
973
+ /**
974
+ * Determines if the heading menu item should be shown
975
+ */
976
+ function shouldShowItem(props) {
977
+ const { editor, level, hideWhenUnavailable } = props;
978
+ if (!editor) return false;
979
+ if (!hideWhenUnavailable) return true;
980
+ if (!editor.isEditable) return false;
981
+ if (!isNodeInSchema("heading", editor)) return false;
982
+ if (!editor.isActive("code")) {
983
+ if (Array.isArray(level)) return level.some((l) => canToggle(editor, l));
984
+ return canToggle(editor, level);
985
+ }
986
+ return true;
987
+ }
988
+ /**
989
+ * Custom hook that provides heading functionality for Tiptap editor
990
+ *
991
+ * @example
992
+ * ```tsx
993
+ * // Simple usage
994
+ * function MySimpleHeadingItem() {
995
+ * const { isVisible, isActive, handleToggle, Icon } = useHeading({ level: 1 })
996
+ *
997
+ * if (!isVisible) return null
998
+ *
999
+ * return (
1000
+ * <button
1001
+ * onClick={handleToggle}
1002
+ * aria-pressed={isActive}
1003
+ * >
1004
+ * <Icon />
1005
+ * Heading 1
1006
+ * </button>
1007
+ * )
1008
+ * }
1009
+ *
1010
+ * // Advanced usage with configuration
1011
+ * function MyAdvancedHeadingItem() {
1012
+ * const { isVisible, isActive, handleToggle, label, Icon } = useHeading({
1013
+ * level: 2,
1014
+ * editor: myEditor,
1015
+ * hideWhenUnavailable: true,
1016
+ * onToggled: (isActive) => console.log('Heading toggled:', isActive)
1017
+ * })
1018
+ *
1019
+ * if (!isVisible) return null
1020
+ *
1021
+ * return (
1022
+ * <MyButton
1023
+ * onClick={handleToggle}
1024
+ * aria-label={label}
1025
+ * aria-pressed={isActive}
1026
+ * >
1027
+ * <Icon />
1028
+ * Toggle Heading 2
1029
+ * </MyButton>
1030
+ * )
1031
+ * }
1032
+ * ```
1033
+ */
1034
+ function useHeading(config) {
1035
+ const { editor: providedEditor, level, hideWhenUnavailable = false, onToggled } = config;
1036
+ const { editor } = useTiptapEditor(providedEditor);
1037
+ const [isVisible, setIsVisible] = (0, react.useState)(true);
1038
+ const canToggleState = canToggle(editor, level);
1039
+ const isActive = isHeadingActive(editor, level);
1040
+ (0, react.useEffect)(() => {
1041
+ if (!editor) return;
1042
+ const handleSelectionUpdate = () => {
1043
+ setIsVisible(shouldShowItem({
1044
+ editor,
1045
+ level,
1046
+ hideWhenUnavailable
1047
+ }));
1048
+ };
1049
+ handleSelectionUpdate();
1050
+ editor.on("selectionUpdate", handleSelectionUpdate);
1051
+ return () => {
1052
+ editor.off("selectionUpdate", handleSelectionUpdate);
1053
+ };
1054
+ }, [
1055
+ editor,
1056
+ level,
1057
+ hideWhenUnavailable
1058
+ ]);
1059
+ return {
1060
+ isVisible,
1061
+ isActive,
1062
+ handleToggle: (0, react.useCallback)(() => {
1063
+ if (!editor) return false;
1064
+ const success = toggleHeading(editor, level);
1065
+ if (success) onToggled?.();
1066
+ return success;
1067
+ }, [
1068
+ editor,
1069
+ level,
1070
+ onToggled
1071
+ ]),
1072
+ canToggle: canToggleState,
1073
+ label: `Titre ${level}`,
1074
+ shortcutKeys: HEADING_SHORTCUT_KEYS[level],
1075
+ Icon: headingIcons[level]
1076
+ };
1077
+ }
1078
+ //#endregion
1079
+ //#region src/tiptap/components/ui/heading-menu-item/heading-menu-item.tsx
1080
+ function HeadingShortcutBadge({ level, shortcutKeys = HEADING_SHORTCUT_KEYS[level] }) {
1081
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material_Badge.default, { children: parseShortcutKeys({ shortcutKeys }) });
1082
+ }
1083
+ /**
1084
+ * Menu item component for toggling heading in a Tiptap editor.
1085
+ *
1086
+ * For custom menu item implementations, use the `useHeading` hook instead.
1087
+ */
1088
+ var HeadingMenuItem = (0, react.forwardRef)(({ editor: providedEditor, level, text, hideWhenUnavailable = false, onToggled, showShortcut = false, onClick, children, ...menuItemProps }, ref) => {
1089
+ const { editor } = useTiptapEditor(providedEditor);
1090
+ const { isVisible, canToggle, isActive, handleToggle, label, Icon, shortcutKeys } = useHeading({
1091
+ editor,
1092
+ level,
1093
+ hideWhenUnavailable,
1094
+ onToggled
1095
+ });
1096
+ const handleClick = (0, react.useCallback)((event) => {
1097
+ onClick?.(event);
1098
+ if (event.defaultPrevented) return;
1099
+ handleToggle();
1100
+ }, [handleToggle, onClick]);
1101
+ if (!isVisible) return null;
1102
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material_MenuItem.default, {
1103
+ component: "li",
1104
+ role: "button",
1105
+ tabIndex: -1,
1106
+ disabled: !canToggle,
1107
+ "data-disabled": !canToggle,
1108
+ "aria-label": label,
1109
+ "aria-pressed": isActive,
1110
+ tooltip: label,
1111
+ selected: isActive,
1112
+ onClick: handleClick,
1113
+ ...menuItemProps,
1114
+ ref,
1115
+ children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1116
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { fontSize: "small" }),
1117
+ text && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material_Typography.default, {
1118
+ component: "span",
1119
+ variant: "body2",
1120
+ sx: { ml: 1 },
1121
+ children: label
1122
+ }),
1123
+ showShortcut && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeadingShortcutBadge, {
1124
+ level,
1125
+ shortcutKeys
1126
+ })
1127
+ ] })
1128
+ });
1129
+ });
1130
+ HeadingMenuItem.displayName = "HeadingMenuItem";
1131
+ //#endregion
1132
+ //#region src/tiptap/components/icons/heading-icon.tsx
1133
+ var HeadingIcon = (0, _mui_material_utils.createSvgIcon)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1134
+ d: "M6 3C6.55228 3 7 3.44772 7 4V11H17V4C17 3.44772 17.4477 3 18 3C18.5523 3 19 3.44772 19 4V20C19 20.5523 18.5523 21 18 21C17.4477 21 17 20.5523 17 20V13H7V20C7 20.5523 6.55228 21 6 21C5.44772 21 5 20.5523 5 20V4C5 3.44772 5.44772 3 6 3Z",
1135
+ fill: "currentColor"
1136
+ }), "Heading");
1137
+ //#endregion
1138
+ //#region src/tiptap/components/ui/heading-menu/use-heading-menu.ts
1139
+ /**
1140
+ * Gets the currently active heading level from the available levels
1141
+ */
1142
+ function getActiveHeadingLevel(editor, levels = [
1143
+ 1,
1144
+ 2,
1145
+ 3,
1146
+ 4,
1147
+ 5,
1148
+ 6
1149
+ ]) {
1150
+ if (!editor || !editor.isEditable) return void 0;
1151
+ return levels.find((level) => isHeadingActive(editor, level));
1152
+ }
1153
+ /**
1154
+ * Custom hook that provides heading menu functionality for Tiptap editor
1155
+ *
1156
+ * @example
1157
+ * ```tsx
1158
+ * // Simple usage
1159
+ * function MyHeading() {
1160
+ * const {
1161
+ * isVisible,
1162
+ * activeLevel,
1163
+ * isAnyHeadingActive,
1164
+ * canToggle,
1165
+ * levels,
1166
+ * } = useHeadingMenu()
1167
+ *
1168
+ * if (!isVisible) return null
1169
+ *
1170
+ * return (
1171
+ * <Menu>
1172
+ * // content
1173
+ * </Menu>
1174
+ * )
1175
+ * }
1176
+ *
1177
+ * // Advanced usage with configuration
1178
+ * function MyAdvancedHeading() {
1179
+ * const {
1180
+ * isVisible,
1181
+ * activeLevel,
1182
+ * } = useHeadingMenu({
1183
+ * editor: myEditor,
1184
+ * levels: [1, 2, 3],
1185
+ * hideWhenUnavailable: true,
1186
+ * })
1187
+ *
1188
+ * // component implementation
1189
+ * }
1190
+ * ```
1191
+ */
1192
+ function useHeadingMenu(config) {
1193
+ const { editor: providedEditor, levels = [
1194
+ 1,
1195
+ 2,
1196
+ 3,
1197
+ 4,
1198
+ 5,
1199
+ 6
1200
+ ], hideWhenUnavailable = false } = config || {};
1201
+ const { editor } = useTiptapEditor(providedEditor);
1202
+ const [isVisible, setIsVisible] = (0, react.useState)(true);
1203
+ const activeLevel = getActiveHeadingLevel(editor, levels);
1204
+ const isActive = isHeadingActive(editor);
1205
+ const canToggleState = canToggle(editor);
1206
+ (0, react.useEffect)(() => {
1207
+ if (!editor) return;
1208
+ const handleSelectionUpdate = () => {
1209
+ setIsVisible(shouldShowItem({
1210
+ editor,
1211
+ hideWhenUnavailable,
1212
+ level: levels
1213
+ }));
1214
+ };
1215
+ handleSelectionUpdate();
1216
+ editor.on("selectionUpdate", handleSelectionUpdate);
1217
+ return () => {
1218
+ editor.off("selectionUpdate", handleSelectionUpdate);
1219
+ };
1220
+ }, [
1221
+ editor,
1222
+ hideWhenUnavailable,
1223
+ levels
1224
+ ]);
1225
+ return {
1226
+ isVisible,
1227
+ activeLevel,
1228
+ isActive,
1229
+ canToggle: canToggleState,
1230
+ levels,
1231
+ label: "Titre",
1232
+ Icon: activeLevel ? headingIcons[activeLevel] : HeadingIcon
1233
+ };
1234
+ }
1235
+ //#endregion
1236
+ //#region src/tiptap/components/ui/heading-menu/heading-menu.tsx
1237
+ /**
1238
+ * Menu component for selecting heading levels in a Tiptap editor.
1239
+ *
1240
+ * For custom menu implementations, use the `useHeadingMenu` hook instead.
1241
+ */
1242
+ var HeadingMenu = (0, react.forwardRef)(({ editor: providedEditor, levels = [
1243
+ 1,
1244
+ 2,
1245
+ 3,
1246
+ 4,
1247
+ 5,
1248
+ 6
1249
+ ], hideWhenUnavailable = false, onOpenChange, children, modal = true, ...buttonProps }, ref) => {
1250
+ const { editor } = useTiptapEditor(providedEditor);
1251
+ const [anchorEl, setAnchorEl] = (0, react.useState)(null);
1252
+ const open = Boolean(anchorEl);
1253
+ const { isVisible, isActive, canToggle, Icon, label } = useHeadingMenu({
1254
+ editor,
1255
+ levels,
1256
+ hideWhenUnavailable
1257
+ });
1258
+ const handleClick = (event) => {
1259
+ setAnchorEl(event.currentTarget);
1260
+ };
1261
+ const handleClose = (0, react.useCallback)(() => {
1262
+ if (!editor || !canToggle) return;
1263
+ setAnchorEl(null);
1264
+ onOpenChange?.(false);
1265
+ }, [
1266
+ canToggle,
1267
+ editor,
1268
+ onOpenChange
1269
+ ]);
1270
+ if (!isVisible) return null;
1271
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
1272
+ id: "heading-button",
1273
+ value: "heading",
1274
+ type: "button",
1275
+ selected: isActive || open,
1276
+ role: "button",
1277
+ tabIndex: -1,
1278
+ disabled: !canToggle,
1279
+ "data-disabled": !canToggle,
1280
+ "aria-haspopup": "listbox",
1281
+ "aria-controls": "heading-menu",
1282
+ "aria-label": "Format text as heading",
1283
+ "aria-expanded": open,
1284
+ tooltip: label,
1285
+ onClick: handleClick,
1286
+ ...buttonProps,
1287
+ ref,
1288
+ children: children ? children : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { fontSize: "small" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExpandMoreRounded_default, { sx: { fontSize: "0.75rem" } })] })
1289
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material_Menu.default, {
1290
+ id: "heading-menu",
1291
+ anchorEl,
1292
+ open,
1293
+ onClose: handleClose,
1294
+ slotProps: { list: {
1295
+ "aria-labelledby": "heading-button",
1296
+ role: "listbox"
1297
+ } },
1298
+ children: levels.map((level) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeadingMenuItem, {
1299
+ editor,
1300
+ level,
1301
+ text: `Heading ${level}`,
1302
+ onClick: handleClose
1303
+ }, `heading-${level}`))
1304
+ })] });
1305
+ });
1306
+ HeadingMenu.displayName = "HeadingMenu";
1307
+ //#endregion
1308
+ //#region ../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.2._02cb1dd638d2ccdea7d16c50ea4ea174/node_modules/@mui/icons-material/KeyboardReturnRounded.mjs
1309
+ var KeyboardReturnRounded_default = (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M19 8v3H5.83l2.88-2.88c.39-.39.39-1.02 0-1.41a.996.996 0 0 0-1.41 0L2.71 11.3c-.39.39-.39 1.02 0 1.41L7.3 17.3c.39.39 1.02.39 1.41 0s.39-1.02 0-1.41L5.83 13H20c.55 0 1-.45 1-1V8c0-.55-.45-1-1-1s-1 .45-1 1" }), "KeyboardReturnRounded");
1310
+ //#endregion
1311
+ //#region ../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.2._02cb1dd638d2ccdea7d16c50ea4ea174/node_modules/@mui/icons-material/LinkRounded.mjs
1312
+ var LinkRounded_default = (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M17 7h-3c-.55 0-1 .45-1 1s.45 1 1 1h3c1.65 0 3 1.35 3 3s-1.35 3-3 3h-3c-.55 0-1 .45-1 1s.45 1 1 1h3c2.76 0 5-2.24 5-5s-2.24-5-5-5m-9 5c0 .55.45 1 1 1h6c.55 0 1-.45 1-1s-.45-1-1-1H9c-.55 0-1 .45-1 1m2 3H7c-1.65 0-3-1.35-3-3s1.35-3 3-3h3c.55 0 1-.45 1-1s-.45-1-1-1H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h3c.55 0 1-.45 1-1s-.45-1-1-1" }), "LinkRounded");
1313
+ //#endregion
1314
+ //#region ../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.2._02cb1dd638d2ccdea7d16c50ea4ea174/node_modules/@mui/icons-material/OpenInNewRounded.mjs
1315
+ var OpenInNewRounded_default = (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M18 19H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h5c.55 0 1-.45 1-1s-.45-1-1-1H5c-1.11 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-6c0-.55-.45-1-1-1s-1 .45-1 1v5c0 .55-.45 1-1 1M14 4c0 .55.45 1 1 1h2.59l-9.13 9.13c-.39.39-.39 1.02 0 1.41s1.02.39 1.41 0L19 6.41V9c0 .55.45 1 1 1s1-.45 1-1V4c0-.55-.45-1-1-1h-5c-.55 0-1 .45-1 1" }), "OpenInNewRounded");
1316
+ //#endregion
1317
+ //#region src/tiptap/components/ui/link-popover/use-link-popover.ts
1318
+ /**
1319
+ * Checks if a link can be set in the current editor state
1320
+ */
1321
+ function canSetLink(editor) {
1322
+ if (!editor || !editor.isEditable) return false;
1323
+ if (isNodeTypeSelected(editor, ["image"], true)) return false;
1324
+ try {
1325
+ return editor.can().setMark("link");
1326
+ } catch {
1327
+ return false;
1328
+ }
1329
+ }
1330
+ /**
1331
+ * Checks if a link is currently active in the editor
1332
+ */
1333
+ function isLinkActive(editor) {
1334
+ if (!editor || !editor.isEditable) return false;
1335
+ return editor.isActive("link");
1336
+ }
1337
+ /**
1338
+ * Determines if the link button should be shown
1339
+ */
1340
+ function shouldShowLinkButton(props) {
1341
+ const { editor, hideWhenUnavailable } = props;
1342
+ if (!editor || !editor.isEditable) return false;
1343
+ const linkInSchema = isMarkInSchema("link", editor);
1344
+ if (!hideWhenUnavailable) return true;
1345
+ if (!linkInSchema) return false;
1346
+ if (!editor.isActive("code")) return canSetLink(editor);
1347
+ return true;
1348
+ }
1349
+ /**
1350
+ * Custom hook for handling link operations in a Tiptap editor
1351
+ */
1352
+ function useLinkHandler(props) {
1353
+ const { editor, onSetLink } = props;
1354
+ const [url, setUrl] = (0, react.useState)(null);
1355
+ (0, react.useEffect)(() => {
1356
+ if (!editor) return;
1357
+ const { href } = editor.getAttributes("link");
1358
+ if (isLinkActive(editor) && url === null) setUrl(href || "");
1359
+ }, [editor, url]);
1360
+ (0, react.useEffect)(() => {
1361
+ if (!editor) return;
1362
+ const updateLinkState = () => {
1363
+ const { href } = editor.getAttributes("link");
1364
+ setUrl(href || "");
1365
+ };
1366
+ editor.on("selectionUpdate", updateLinkState);
1367
+ return () => {
1368
+ editor.off("selectionUpdate", updateLinkState);
1369
+ };
1370
+ }, [editor]);
1371
+ const setLink = (0, react.useCallback)(() => {
1372
+ if (!url || !editor) return;
1373
+ const { selection } = editor.state;
1374
+ const isEmpty = selection.empty;
1375
+ let chain = editor.chain().focus();
1376
+ chain = chain.extendMarkRange("link").setLink({ href: url });
1377
+ if (isEmpty) chain = chain.insertContent({
1378
+ type: "text",
1379
+ text: url
1380
+ });
1381
+ chain.run();
1382
+ setUrl(null);
1383
+ onSetLink?.();
1384
+ }, [
1385
+ editor,
1386
+ onSetLink,
1387
+ url
1388
+ ]);
1389
+ const removeLink = (0, react.useCallback)(() => {
1390
+ if (!editor) return;
1391
+ editor.chain().focus().extendMarkRange("link").unsetLink().setMeta("preventAutolink", true).run();
1392
+ setUrl("");
1393
+ }, [editor]);
1394
+ const openLink = (0, react.useCallback)((target = "_blank", features = "noopener,noreferrer") => {
1395
+ if (!url) return;
1396
+ const safeUrl = sanitizeUrl(url, window.location.href);
1397
+ if (safeUrl !== "#") window.open(safeUrl, target, features);
1398
+ }, [url]);
1399
+ return {
1400
+ url: url || "",
1401
+ setUrl,
1402
+ setLink,
1403
+ removeLink,
1404
+ openLink
1405
+ };
1406
+ }
1407
+ /**
1408
+ * Custom hook for link popover state management
1409
+ */
1410
+ function useLinkState(props) {
1411
+ const { editor, hideWhenUnavailable = false } = props;
1412
+ const canSet = canSetLink(editor);
1413
+ const isActive = isLinkActive(editor);
1414
+ const [isVisible, setIsVisible] = (0, react.useState)(true);
1415
+ (0, react.useEffect)(() => {
1416
+ if (!editor) return;
1417
+ const handleSelectionUpdate = () => {
1418
+ setIsVisible(shouldShowLinkButton({
1419
+ editor,
1420
+ hideWhenUnavailable
1421
+ }));
1422
+ };
1423
+ handleSelectionUpdate();
1424
+ editor.on("selectionUpdate", handleSelectionUpdate);
1425
+ return () => {
1426
+ editor.off("selectionUpdate", handleSelectionUpdate);
1427
+ };
1428
+ }, [editor, hideWhenUnavailable]);
1429
+ return {
1430
+ isVisible,
1431
+ canSet,
1432
+ isActive
1433
+ };
1434
+ }
1435
+ /**
1436
+ * Main hook that provides link popover functionality for Tiptap editor
1437
+ *
1438
+ * @example
1439
+ * ```tsx
1440
+ * // Simple usage
1441
+ * function MyLinkButton() {
1442
+ * const { isVisible, canSet, isActive, Icon, label } = useLinkPopover()
1443
+ *
1444
+ * if (!isVisible) return null
1445
+ *
1446
+ * return <button disabled={!canSet}>Link</button>
1447
+ * }
1448
+ *
1449
+ * // Advanced usage with configuration
1450
+ * function MyAdvancedLinkButton() {
1451
+ * const { isVisible, canSet, isActive, Icon, label } = useLinkPopover({
1452
+ * editor: myEditor,
1453
+ * hideWhenUnavailable: true,
1454
+ * onSetLink: () => console.log('Link set!')
1455
+ * })
1456
+ *
1457
+ * if (!isVisible) return null
1458
+ *
1459
+ * return (
1460
+ * <MyButton
1461
+ * disabled={!canSet}
1462
+ * aria-label={label}
1463
+ * aria-pressed={isActive}
1464
+ * >
1465
+ * <Icon />
1466
+ * {label}
1467
+ * </MyButton>
1468
+ * )
1469
+ * }
1470
+ * ```
1471
+ */
1472
+ function useLinkPopover(config) {
1473
+ const { editor: providedEditor, hideWhenUnavailable = false, onSetLink } = config || {};
1474
+ const { editor } = useTiptapEditor(providedEditor);
1475
+ const { isVisible, canSet, isActive } = useLinkState({
1476
+ editor,
1477
+ hideWhenUnavailable
1478
+ });
1479
+ return {
1480
+ isVisible,
1481
+ canSet,
1482
+ isActive,
1483
+ label: "Lien",
1484
+ Icon: LinkRounded_default,
1485
+ ...useLinkHandler({
1486
+ editor,
1487
+ onSetLink
1488
+ })
1489
+ };
1490
+ }
1491
+ //#endregion
1492
+ //#region src/tiptap/components/ui/link-popover/link-popover.tsx
1493
+ /**
1494
+ * Link button component for triggering the link popover
1495
+ */
1496
+ var LinkButton = (0, react.forwardRef)(({ className, children, ...props }, ref) => {
1497
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
1498
+ type: "button",
1499
+ className,
1500
+ role: "button",
1501
+ tabIndex: -1,
1502
+ ref,
1503
+ ...props,
1504
+ children: children || /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LinkRounded_default, { fontSize: "small" })
1505
+ });
1506
+ });
1507
+ LinkButton.displayName = "LinkButton";
1508
+ var StyledIconButton = (0, _mui_material_styles.styled)(_mui_material_IconButton.default)(({ theme }) => ({
1509
+ borderRadius: theme.shape.borderRadius,
1510
+ "& .MuiTouchRipple-root .MuiTouchRipple-child": { borderRadius: theme.shape.borderRadius }
1511
+ }));
1512
+ /**
1513
+ * Main content component for the link popover
1514
+ */
1515
+ var LinkMain = ({ url, setUrl, setLink, removeLink, openLink, isActive }) => {
1516
+ const handleKeyDown = (event) => {
1517
+ if (event.key === "Enter") {
1518
+ event.preventDefault();
1519
+ setLink();
1520
+ }
1521
+ };
1522
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material_Stack.default, {
1523
+ direction: "row",
1524
+ spacing: .5,
1525
+ sx: {
1526
+ alignItems: "center",
1527
+ p: 1
1528
+ },
1529
+ children: [
1530
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material_InputBase.default, {
1531
+ type: "url",
1532
+ placeholder: "Coller un lien...",
1533
+ value: url,
1534
+ onChange: (e) => setUrl(e.target.value),
1535
+ onKeyDown: handleKeyDown,
1536
+ autoFocus: true,
1537
+ autoComplete: "off",
1538
+ autoCorrect: "off",
1539
+ autoCapitalize: "off",
1540
+ sx: {
1541
+ padding: ".25rem .625rem",
1542
+ fontSize: "0.875rem",
1543
+ minWidth: "12rem"
1544
+ }
1545
+ }),
1546
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledIconButton, {
1547
+ size: "small",
1548
+ type: "button",
1549
+ onClick: setLink,
1550
+ title: "Appliquer le lien",
1551
+ disabled: !url && !isActive,
1552
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyboardReturnRounded_default, { fontSize: "small" })
1553
+ }),
1554
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material_Divider.default, {
1555
+ orientation: "vertical",
1556
+ flexItem: true,
1557
+ sx: { my: .5 }
1558
+ }),
1559
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledIconButton, {
1560
+ size: "small",
1561
+ type: "button",
1562
+ onClick: openLink,
1563
+ title: "Ouvrir dans une nouvelle fenêtre",
1564
+ disabled: !url && !isActive,
1565
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OpenInNewRounded_default, { fontSize: "small" })
1566
+ }),
1567
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledIconButton, {
1568
+ size: "small",
1569
+ type: "button",
1570
+ onClick: removeLink,
1571
+ title: "Supprimer le lien",
1572
+ disabled: !url && !isActive,
1573
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DeleteRounded_default, { fontSize: "small" })
1574
+ })
1575
+ ]
1576
+ });
1577
+ };
1578
+ /**
1579
+ * Link popover component for Tiptap editors.
1580
+ *
1581
+ * For custom popover implementations, use the `useLinkPopover` hook instead.
1582
+ */
1583
+ var LinkPopover = (0, react.forwardRef)(({ editor: providedEditor, hideWhenUnavailable = false, onSetLink, onOpenChange, autoOpenOnLinkActive = true, onClick, children, ...buttonProps }, ref) => {
1584
+ const { editor } = useTiptapEditor(providedEditor);
1585
+ const [anchorEl, setAnchorEl] = (0, react.useState)(null);
1586
+ const internalRef = (0, react.useRef)(null);
1587
+ const justSubmittedRef = (0, react.useRef)(false);
1588
+ const { isVisible, canSet, isActive, url, setUrl, setLink, removeLink, openLink, label, Icon } = useLinkPopover({
1589
+ editor,
1590
+ hideWhenUnavailable,
1591
+ onSetLink
1592
+ });
1593
+ const setRefs = (0, react.useCallback)((node) => {
1594
+ internalRef.current = node;
1595
+ if (typeof ref === "function") ref(node);
1596
+ else if (ref) ref.current = node;
1597
+ }, [ref]);
1598
+ const handleClose = (0, react.useCallback)(() => {
1599
+ setAnchorEl(null);
1600
+ onOpenChange?.(false);
1601
+ }, [onOpenChange]);
1602
+ const handleSetLink = (0, react.useCallback)(() => {
1603
+ justSubmittedRef.current = true;
1604
+ setLink();
1605
+ setAnchorEl(null);
1606
+ queueMicrotask(() => {
1607
+ justSubmittedRef.current = false;
1608
+ });
1609
+ }, [setLink]);
1610
+ const handleClick = (0, react.useCallback)((event) => {
1611
+ onClick?.(event, !isActive);
1612
+ if (event.defaultPrevented) return;
1613
+ setAnchorEl(event.currentTarget);
1614
+ }, [onClick, anchorEl]);
1615
+ (0, react.useEffect)(() => {
1616
+ if (autoOpenOnLinkActive && isActive && !justSubmittedRef.current) setAnchorEl(internalRef.current);
1617
+ }, [autoOpenOnLinkActive, isActive]);
1618
+ if (!isVisible) return null;
1619
+ const open = Boolean(anchorEl);
1620
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LinkButton, {
1621
+ value: "link",
1622
+ disabled: !canSet,
1623
+ selected: isActive || open,
1624
+ "data-disabled": !canSet,
1625
+ "aria-label": label,
1626
+ tooltip: label,
1627
+ onClick: handleClick,
1628
+ ...buttonProps,
1629
+ ref: setRefs,
1630
+ children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { fontSize: "small" })
1631
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material_Popover.default, {
1632
+ open,
1633
+ anchorEl,
1634
+ onClose: handleClose,
1635
+ anchorOrigin: {
1636
+ vertical: "bottom",
1637
+ horizontal: "center"
1638
+ },
1639
+ transformOrigin: {
1640
+ vertical: -4,
1641
+ horizontal: "center"
1642
+ },
1643
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LinkMain, {
1644
+ url,
1645
+ setUrl,
1646
+ setLink: handleSetLink,
1647
+ removeLink,
1648
+ openLink,
1649
+ isActive
1650
+ })
1651
+ })] });
1652
+ });
1653
+ LinkPopover.displayName = "LinkPopover";
1654
+ //#endregion
1655
+ //#region src/tiptap/components/ui/list-button/use-list.ts
1656
+ var listIcons = {
1657
+ bulletList: (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5m0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5m0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5M8 19h12c.55 0 1-.45 1-1s-.45-1-1-1H8c-.55 0-1 .45-1 1s.45 1 1 1m0-6h12c.55 0 1-.45 1-1s-.45-1-1-1H8c-.55 0-1 .45-1 1s.45 1 1 1M7 6c0 .55.45 1 1 1h12c.55 0 1-.45 1-1s-.45-1-1-1H8c-.55 0-1 .45-1 1" }), "FormatListBulletedRounded"),
1658
+ orderedList: (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M8 7h12c.55 0 1-.45 1-1s-.45-1-1-1H8c-.55 0-1 .45-1 1s.45 1 1 1m12 10H8c-.55 0-1 .45-1 1s.45 1 1 1h12c.55 0 1-.45 1-1s-.45-1-1-1m0-6H8c-.55 0-1 .45-1 1s.45 1 1 1h12c.55 0 1-.45 1-1s-.45-1-1-1M4.5 16h-2c-.28 0-.5.22-.5.5s.22.5.5.5H4v.5h-.5c-.28 0-.5.22-.5.5s.22.5.5.5H4v.5H2.5c-.28 0-.5.22-.5.5s.22.5.5.5h2c.28 0 .5-.22.5-.5v-3c0-.28-.22-.5-.5-.5m-2-11H3v2.5c0 .28.22.5.5.5s.5-.22.5-.5v-3c0-.28-.22-.5-.5-.5h-1c-.28 0-.5.22-.5.5s.22.5.5.5m2 5h-2c-.28 0-.5.22-.5.5s.22.5.5.5h1.3l-1.68 1.96c-.08.09-.12.21-.12.32v.22c0 .28.22.5.5.5h2c.28 0 .5-.22.5-.5s-.22-.5-.5-.5H3.2l1.68-1.96c.08-.09.12-.21.12-.32v-.22c0-.28-.22-.5-.5-.5" }), "FormatListNumberedRounded"),
1659
+ taskList: (0, _mui_material_utils.createSvgIcon)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", { children: [
1660
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1661
+ fillRule: "evenodd",
1662
+ clipRule: "evenodd",
1663
+ d: "M2 6C2 4.89543 2.89543 4 4 4H8C9.10457 4 10 4.89543 10 6V10C10 11.1046 9.10457 12 8 12H4C2.89543 12 2 11.1046 2 10V6ZM8 6H4V10H8V6Z",
1664
+ fill: "currentColor"
1665
+ }),
1666
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1667
+ fillRule: "evenodd",
1668
+ clipRule: "evenodd",
1669
+ d: "M9.70711 14.2929C10.0976 14.6834 10.0976 15.3166 9.70711 15.7071L5.70711 19.7071C5.31658 20.0976 4.68342 20.0976 4.29289 19.7071L2.29289 17.7071C1.90237 17.3166 1.90237 16.6834 2.29289 16.2929C2.68342 15.9024 3.31658 15.9024 3.70711 16.2929L5 17.5858L8.29289 14.2929C8.68342 13.9024 9.31658 13.9024 9.70711 14.2929Z",
1670
+ fill: "currentColor"
1671
+ }),
1672
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1673
+ fillRule: "evenodd",
1674
+ clipRule: "evenodd",
1675
+ d: "M12 6C12 5.44772 12.4477 5 13 5H21C21.5523 5 22 5.44772 22 6C22 6.55228 21.5523 7 21 7H13C12.4477 7 12 6.55228 12 6Z",
1676
+ fill: "currentColor"
1677
+ }),
1678
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1679
+ fillRule: "evenodd",
1680
+ clipRule: "evenodd",
1681
+ d: "M12 12C12 11.4477 12.4477 11 13 11H21C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13H13C12.4477 13 12 12.5523 12 12Z",
1682
+ fill: "currentColor"
1683
+ }),
1684
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1685
+ fillRule: "evenodd",
1686
+ clipRule: "evenodd",
1687
+ d: "M12 18C12 17.4477 12.4477 17 13 17H21C21.5523 17 22 17.4477 22 18C22 18.5523 21.5523 19 21 19H13C12.4477 19 12 18.5523 12 18Z",
1688
+ fill: "currentColor"
1689
+ })
1690
+ ] }), "ListTodo")
1691
+ };
1692
+ var listLabels = {
1693
+ bulletList: "Liste à puces",
1694
+ orderedList: "Liste numérotée",
1695
+ taskList: "Liste de tâches"
1696
+ };
1697
+ var LIST_SHORTCUT_KEYS = {
1698
+ bulletList: "mod+shift+8",
1699
+ orderedList: "mod+shift+7",
1700
+ taskList: "mod+shift+9"
1701
+ };
1702
+ /**
1703
+ * Checks if a list can be toggled in the current editor state
1704
+ */
1705
+ function canToggleList(editor, type, turnInto = true) {
1706
+ if (!editor || !editor.isEditable) return false;
1707
+ if (!isNodeInSchema(type, editor) || isNodeTypeSelected(editor, ["image"])) return false;
1708
+ if (!turnInto) switch (type) {
1709
+ case "bulletList": return editor.can().toggleBulletList();
1710
+ case "orderedList": return editor.can().toggleOrderedList();
1711
+ case "taskList": return editor.can().toggleList("taskList", "taskItem");
1712
+ default: return false;
1713
+ }
1714
+ if (!selectionWithinConvertibleTypes(editor, [
1715
+ "paragraph",
1716
+ "heading",
1717
+ "bulletList",
1718
+ "orderedList",
1719
+ "taskList",
1720
+ "blockquote",
1721
+ "codeBlock"
1722
+ ])) return false;
1723
+ switch (type) {
1724
+ case "bulletList": return editor.can().toggleBulletList() || editor.can().clearNodes();
1725
+ case "orderedList": return editor.can().toggleOrderedList() || editor.can().clearNodes();
1726
+ case "taskList": return editor.can().toggleList("taskList", "taskItem") || editor.can().clearNodes();
1727
+ default: return false;
1728
+ }
1729
+ }
1730
+ /**
1731
+ * Checks if list is currently active
1732
+ */
1733
+ function isListActive(editor, type) {
1734
+ if (!editor || !editor.isEditable) return false;
1735
+ switch (type) {
1736
+ case "bulletList": return editor.isActive("bulletList");
1737
+ case "orderedList": return editor.isActive("orderedList");
1738
+ case "taskList": return editor.isActive("taskList");
1739
+ default: return false;
1740
+ }
1741
+ }
1742
+ /**
1743
+ * Toggles list in the editor
1744
+ */
1745
+ function toggleList(editor, type) {
1746
+ if (!editor || !editor.isEditable) return false;
1747
+ if (!canToggleList(editor, type)) return false;
1748
+ try {
1749
+ const view = editor.view;
1750
+ let state = view.state;
1751
+ let tr = state.tr;
1752
+ const blocks = getSelectedBlockNodes(editor);
1753
+ const isPossibleToTurnInto = selectionWithinConvertibleTypes(editor, [
1754
+ "paragraph",
1755
+ "heading",
1756
+ "bulletList",
1757
+ "orderedList",
1758
+ "taskList",
1759
+ "blockquote",
1760
+ "codeBlock"
1761
+ ]) && blocks.length === 1;
1762
+ if ((state.selection.empty || state.selection instanceof _tiptap_pm_state.TextSelection) && isPossibleToTurnInto) {
1763
+ const pos = findNodePosition({
1764
+ editor,
1765
+ node: state.selection.$anchor.node(1)
1766
+ })?.pos;
1767
+ if (!isValidPosition(pos)) return false;
1768
+ tr = tr.setSelection(_tiptap_pm_state.NodeSelection.create(state.doc, pos));
1769
+ view.dispatch(tr);
1770
+ state = view.state;
1771
+ }
1772
+ const selection = state.selection;
1773
+ let chain = editor.chain().focus();
1774
+ if (selection instanceof _tiptap_pm_state.NodeSelection) {
1775
+ const firstChild = selection.node.firstChild?.firstChild;
1776
+ const lastChild = selection.node.lastChild?.lastChild;
1777
+ const from = firstChild ? selection.from + firstChild.nodeSize : selection.from + 1;
1778
+ const to = lastChild ? selection.to - lastChild.nodeSize : selection.to - 1;
1779
+ const resolvedFrom = state.doc.resolve(from);
1780
+ const resolvedTo = state.doc.resolve(to);
1781
+ chain = chain.setTextSelection(_tiptap_pm_state.TextSelection.between(resolvedFrom, resolvedTo)).clearNodes();
1782
+ }
1783
+ if (editor.isActive(type)) chain.liftListItem("listItem").lift("bulletList").lift("orderedList").run();
1784
+ else {
1785
+ const toggle = {
1786
+ bulletList: () => chain.toggleBulletList(),
1787
+ orderedList: () => chain.toggleOrderedList(),
1788
+ taskList: () => chain.toggleList("taskList", "taskItem")
1789
+ }[type];
1790
+ if (!toggle) return false;
1791
+ toggle().run();
1792
+ }
1793
+ editor.chain().focus().selectTextblockEnd().run();
1794
+ return true;
1795
+ } catch {
1796
+ return false;
1797
+ }
1798
+ }
1799
+ /**
1800
+ * Determines if the list button should be shown
1801
+ */
1802
+ function shouldShowButton$3(props) {
1803
+ const { editor, type, hideWhenUnavailable } = props;
1804
+ if (!editor) return false;
1805
+ if (!hideWhenUnavailable) return true;
1806
+ if (!editor.isEditable) return false;
1807
+ if (!isNodeInSchema(type, editor)) return false;
1808
+ if (!editor.isActive("code")) return canToggleList(editor, type);
1809
+ return true;
1810
+ }
1811
+ /**
1812
+ * Custom hook that provides list functionality for Tiptap editor
1813
+ *
1814
+ * @example
1815
+ * ```tsx
1816
+ * // Simple usage
1817
+ * function MySimpleListButton() {
1818
+ * const { isVisible, handleToggle, isActive } = useList({ type: "bulletList" })
1819
+ *
1820
+ * if (!isVisible) return null
1821
+ *
1822
+ * return <button onClick={handleToggle}>Bullet List</button>
1823
+ * }
1824
+ *
1825
+ * // Advanced usage with configuration
1826
+ * function MyAdvancedListButton() {
1827
+ * const { isVisible, handleToggle, label, isActive } = useList({
1828
+ * type: "orderedList",
1829
+ * editor: myEditor,
1830
+ * hideWhenUnavailable: true,
1831
+ * onToggled: () => console.log('List toggled!')
1832
+ * })
1833
+ *
1834
+ * if (!isVisible) return null
1835
+ *
1836
+ * return (
1837
+ * <MyButton
1838
+ * onClick={handleToggle}
1839
+ * aria-label={label}
1840
+ * aria-pressed={isActive}
1841
+ * >
1842
+ * Toggle List
1843
+ * </MyButton>
1844
+ * )
1845
+ * }
1846
+ * ```
1847
+ */
1848
+ function useList(config) {
1849
+ const { editor: providedEditor, type, hideWhenUnavailable = false, onToggled } = config;
1850
+ const { editor } = useTiptapEditor(providedEditor);
1851
+ const [isVisible, setIsVisible] = (0, react.useState)(true);
1852
+ const canToggle = canToggleList(editor, type);
1853
+ const isActive = isListActive(editor, type);
1854
+ (0, react.useEffect)(() => {
1855
+ if (!editor) return;
1856
+ const handleSelectionUpdate = () => {
1857
+ setIsVisible(shouldShowButton$3({
1858
+ editor,
1859
+ type,
1860
+ hideWhenUnavailable
1861
+ }));
1862
+ };
1863
+ handleSelectionUpdate();
1864
+ editor.on("selectionUpdate", handleSelectionUpdate);
1865
+ return () => {
1866
+ editor.off("selectionUpdate", handleSelectionUpdate);
1867
+ };
1868
+ }, [
1869
+ editor,
1870
+ type,
1871
+ hideWhenUnavailable
1872
+ ]);
1873
+ return {
1874
+ isVisible,
1875
+ isActive,
1876
+ handleToggle: (0, react.useCallback)(() => {
1877
+ if (!editor) return false;
1878
+ const success = toggleList(editor, type);
1879
+ if (success) onToggled?.();
1880
+ return success;
1881
+ }, [
1882
+ editor,
1883
+ type,
1884
+ onToggled
1885
+ ]),
1886
+ canToggle,
1887
+ label: listLabels[type],
1888
+ shortcutKeys: LIST_SHORTCUT_KEYS[type],
1889
+ Icon: listIcons[type]
1890
+ };
1891
+ }
1892
+ //#endregion
1893
+ //#region src/tiptap/components/ui/list-button/list-button.tsx
1894
+ function ListShortcutBadge({ type, shortcutKeys = LIST_SHORTCUT_KEYS[type] }) {
1895
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material_Badge.default, { children: parseShortcutKeys({ shortcutKeys }) });
1896
+ }
1897
+ /**
1898
+ * Button component for toggling lists in a Tiptap editor.
1899
+ *
1900
+ * For custom button implementations, use the `useList` hook instead.
1901
+ */
1902
+ var ListButton = (0, react.forwardRef)(({ editor: providedEditor, type, text, hideWhenUnavailable = false, onToggled, showShortcut = false, onClick, children, ...buttonProps }, ref) => {
1903
+ const { editor } = useTiptapEditor(providedEditor);
1904
+ const { isVisible, canToggle, isActive, handleToggle, label, shortcutKeys, Icon } = useList({
1905
+ editor,
1906
+ type,
1907
+ hideWhenUnavailable,
1908
+ onToggled
1909
+ });
1910
+ const handleClick = (0, react.useCallback)((event) => {
1911
+ onClick?.(event, !isActive);
1912
+ if (event.defaultPrevented) return;
1913
+ handleToggle();
1914
+ }, [handleToggle, onClick]);
1915
+ if (!isVisible) return null;
1916
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
1917
+ value: type,
1918
+ type: "button",
1919
+ selected: isActive,
1920
+ role: "button",
1921
+ tabIndex: -1,
1922
+ disabled: !canToggle,
1923
+ "data-disabled": !canToggle,
1924
+ "aria-label": label,
1925
+ tooltip: label,
1926
+ onClick: handleClick,
1927
+ ...buttonProps,
1928
+ ref,
1929
+ children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1930
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { fontSize: "small" }),
1931
+ text && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1932
+ className: "tiptap-button-text",
1933
+ children: text
1934
+ }),
1935
+ showShortcut && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ListShortcutBadge, {
1936
+ type,
1937
+ shortcutKeys
1938
+ })
1939
+ ] })
1940
+ });
1941
+ });
1942
+ ListButton.displayName = "ListButton";
1943
+ //#endregion
1944
+ //#region ../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.2._02cb1dd638d2ccdea7d16c50ea4ea174/node_modules/@mui/icons-material/CodeRounded.mjs
1945
+ var CodeRounded_default = (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M8.7 15.9 4.8 12l3.9-3.9c.39-.39.39-1.01 0-1.4a.984.984 0 0 0-1.4 0l-4.59 4.59c-.39.39-.39 1.02 0 1.41l4.59 4.6c.39.39 1.01.39 1.4 0s.39-1.01 0-1.4m6.6 0 3.9-3.9-3.9-3.9a.984.984 0 0 1 0-1.4c.39-.39 1.01-.39 1.4 0l4.59 4.59c.39.39.39 1.02 0 1.41l-4.59 4.6c-.39.39-1.01.39-1.4 0a.984.984 0 0 1 0-1.4" }), "CodeRounded");
1946
+ //#endregion
1947
+ //#region ../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.2._02cb1dd638d2ccdea7d16c50ea4ea174/node_modules/@mui/icons-material/FormatBoldRounded.mjs
1948
+ var FormatBoldRounded_default = (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M15.6 10.79c.97-.67 1.65-1.77 1.65-2.79 0-2.26-1.75-4-4-4H8c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h5.78c2.07 0 3.96-1.69 3.97-3.77.01-1.53-.85-2.84-2.15-3.44M10 6.5h3c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5h-3zm3.5 9H10v-3h3.5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5" }), "FormatBoldRounded");
1949
+ //#endregion
1950
+ //#region ../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.2._02cb1dd638d2ccdea7d16c50ea4ea174/node_modules/@mui/icons-material/FormatItalicRounded.mjs
1951
+ var FormatItalicRounded_default = (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M10 5.5c0 .83.67 1.5 1.5 1.5h.71l-3.42 8H7.5c-.83 0-1.5.67-1.5 1.5S6.67 18 7.5 18h5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5h-.71l3.42-8h1.29c.83 0 1.5-.67 1.5-1.5S17.33 4 16.5 4h-5c-.83 0-1.5.67-1.5 1.5" }), "FormatItalicRounded");
1952
+ //#endregion
1953
+ //#region ../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.2._02cb1dd638d2ccdea7d16c50ea4ea174/node_modules/@mui/icons-material/FormatUnderlinedRounded.mjs
1954
+ var FormatUnderlinedRounded_default = (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M12.79 16.95c3.03-.39 5.21-3.11 5.21-6.16V4.25C18 3.56 17.44 3 16.75 3s-1.25.56-1.25 1.25v6.65c0 1.67-1.13 3.19-2.77 3.52-2.25.47-4.23-1.25-4.23-3.42V4.25C8.5 3.56 7.94 3 7.25 3S6 3.56 6 4.25V11c0 3.57 3.13 6.42 6.79 5.95M5 20c0 .55.45 1 1 1h12c.55 0 1-.45 1-1s-.45-1-1-1H6c-.55 0-1 .45-1 1" }), "FormatUnderlinedRounded");
1955
+ //#endregion
1956
+ //#region ../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.2._02cb1dd638d2ccdea7d16c50ea4ea174/node_modules/@mui/icons-material/StrikethroughSRounded.mjs
1957
+ var StrikethroughSRounded_default = (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M14.59 7.52c0-.31-.05-.59-.15-.85-.09-.27-.24-.49-.44-.68s-.45-.33-.75-.44c-.3-.1-.66-.16-1.06-.16-.39 0-.74.04-1.03.13s-.53.21-.72.36c-.19.16-.34.34-.44.55q-.15.315-.15.66c0 .48.25.88.74 1.21.38.25.77.48 1.41.7H7.39c-.05-.08-.11-.17-.15-.25-.26-.48-.39-1.03-.39-1.67 0-.61.13-1.16.4-1.67q.39-.75 1.11-1.29c.48-.35 1.05-.63 1.7-.83.66-.19 1.39-.29 2.18-.29.81 0 1.54.11 2.21.34.66.22 1.23.54 1.69.94.47.4.83.88 1.08 1.43s.38 1.15.38 1.81zM20 10H4c-.55 0-1 .45-1 1s.45 1 1 1h8.62c.18.07.4.14.55.2q.555.255.87.51c.315.255.35.36.43.57.07.2.11.43.11.69 0 .23-.05.45-.14.66-.09.2-.23.38-.42.53s-.42.26-.71.35c-.29.08-.63.13-1.01.13-.43 0-.83-.04-1.18-.13s-.66-.23-.91-.42-.45-.44-.59-.75-.25-.76-.25-1.21H6.4c0 .55.08 1.13.24 1.58s.37.85.65 1.21c.28.35.6.66.98.92.37.26.78.48 1.22.65q.66.255 1.38.39c.48.08.96.13 1.44.13.8 0 1.53-.09 2.18-.28s1.21-.45 1.67-.79.82-.77 1.07-1.27.38-1.07.38-1.71c0-.6-.1-1.14-.31-1.61-.05-.11-.11-.23-.17-.33H20c.55 0 1-.45 1-1V11c0-.55-.45-1-1-1" }), "StrikethroughSRounded");
1958
+ //#endregion
1959
+ //#region ../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.2._02cb1dd638d2ccdea7d16c50ea4ea174/node_modules/@mui/icons-material/SubscriptRounded.mjs
1960
+ var SubscriptRounded_default = (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M10.52 10.73 7.3 5.72C6.82 4.97 7.35 4 8.23 4c.39 0 .74.2.95.53l2.76 4.46h.12l2.74-4.45c.21-.34.57-.54.96-.54.88 0 1.42.98.94 1.72l-3.23 5 3.55 5.55c.48.75-.06 1.73-.94 1.73-.38 0-.74-.2-.95-.52l-3.07-4.89h-.12l-3.07 4.89c-.2.32-.56.52-.95.52-.88 0-1.42-.97-.94-1.72zM23 19.5c0-.28-.22-.5-.5-.5H20v-1h2c.55 0 1-.45 1-1v-1c0-.55-.45-1-1-1h-2.5c-.28 0-.5.22-.5.5s.22.5.5.5H22v1h-2c-.55 0-1 .45-1 1v1c0 .55.45 1 1 1h2.5c.28 0 .5-.22.5-.5" }), "SubscriptRounded");
1961
+ //#endregion
1962
+ //#region src/tiptap/components/ui/mark-button/use-mark.ts
1963
+ var markIcons = {
1964
+ bold: FormatBoldRounded_default,
1965
+ italic: FormatItalicRounded_default,
1966
+ underline: FormatUnderlinedRounded_default,
1967
+ strike: StrikethroughSRounded_default,
1968
+ code: CodeRounded_default,
1969
+ superscript: (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M10.51 12.73 7.3 7.72C6.82 6.97 7.35 6 8.23 6c.39 0 .74.2.95.53l2.76 4.46h.12l2.74-4.45c.2-.34.56-.54.95-.54.88 0 1.42.98.94 1.72l-3.23 5 3.55 5.55c.49.75-.05 1.73-.93 1.73-.38 0-.74-.2-.95-.52l-3.07-4.89h-.12l-3.07 4.89c-.21.32-.56.52-.95.52-.88 0-1.42-.97-.94-1.72zM23 8.5c0-.28-.22-.5-.5-.5H20V7h2c.55 0 1-.45 1-1V5c0-.55-.45-1-1-1h-2.5c-.28 0-.5.22-.5.5s.22.5.5.5H22v1h-2c-.55 0-1 .45-1 1v1c0 .55.45 1 1 1h2.5c.28 0 .5-.22.5-.5" }), "SuperscriptRounded"),
1970
+ subscript: SubscriptRounded_default
1971
+ };
1972
+ var MARK_SHORTCUT_KEYS = {
1973
+ bold: "mod+b",
1974
+ italic: "mod+i",
1975
+ underline: "mod+u",
1976
+ strike: "mod+shift+s",
1977
+ code: "mod+e",
1978
+ superscript: "mod+.",
1979
+ subscript: "mod+,"
1980
+ };
1981
+ /**
1982
+ * Checks if a mark can be toggled in the current editor state
1983
+ */
1984
+ function canToggleMark(editor, type) {
1985
+ if (!editor || !editor.isEditable) return false;
1986
+ if (!isMarkInSchema(type, editor) || isNodeTypeSelected(editor, ["image"])) return false;
1987
+ return editor.can().toggleMark(type);
1988
+ }
1989
+ /**
1990
+ * Checks if a mark is currently active
1991
+ */
1992
+ function isMarkActive(editor, type) {
1993
+ if (!editor || !editor.isEditable) return false;
1994
+ return editor.isActive(type);
1995
+ }
1996
+ /**
1997
+ * Toggles a mark in the editor
1998
+ */
1999
+ function toggleMark(editor, type) {
2000
+ if (!editor || !editor.isEditable) return false;
2001
+ if (!canToggleMark(editor, type)) return false;
2002
+ return editor.chain().focus().toggleMark(type).run();
2003
+ }
2004
+ /**
2005
+ * Determines if the mark button should be shown
2006
+ */
2007
+ function shouldShowButton$2(props) {
2008
+ const { editor, type, hideWhenUnavailable } = props;
2009
+ if (!editor) return false;
2010
+ if (!hideWhenUnavailable) return true;
2011
+ if (!editor.isEditable) return false;
2012
+ if (!isMarkInSchema(type, editor)) return false;
2013
+ if (!editor.isActive("code")) return canToggleMark(editor, type);
2014
+ return true;
2015
+ }
2016
+ /**
2017
+ * Gets the formatted mark name
2018
+ */
2019
+ function getFormattedMarkName(type) {
2020
+ switch (type) {
2021
+ case "bold": return "Gras";
2022
+ case "italic": return "Italique";
2023
+ case "strike": return "Barré";
2024
+ case "code": return "Code";
2025
+ case "underline": return "Souligné";
2026
+ case "superscript": return "Exposant";
2027
+ case "subscript": return "Indice";
2028
+ }
2029
+ }
2030
+ /**
2031
+ * Custom hook that provides mark functionality for Tiptap editor
2032
+ *
2033
+ * @example
2034
+ * ```tsx
2035
+ * // Simple usage
2036
+ * function MySimpleBoldButton() {
2037
+ * const { isVisible, handleMark } = useMark({ type: "bold" })
2038
+ *
2039
+ * if (!isVisible) return null
2040
+ *
2041
+ * return <button onClick={handleMark}>Bold</button>
2042
+ * }
2043
+ *
2044
+ * // Advanced usage with configuration
2045
+ * function MyAdvancedItalicButton() {
2046
+ * const { isVisible, handleMark, label, isActive } = useMark({
2047
+ * editor: myEditor,
2048
+ * type: "italic",
2049
+ * hideWhenUnavailable: true,
2050
+ * onToggled: () => console.log('Mark toggled!')
2051
+ * })
2052
+ *
2053
+ * if (!isVisible) return null
2054
+ *
2055
+ * return (
2056
+ * <MyButton
2057
+ * onClick={handleMark}
2058
+ * aria-pressed={isActive}
2059
+ * aria-label={label}
2060
+ * >
2061
+ * Italic
2062
+ * </MyButton>
2063
+ * )
2064
+ * }
2065
+ * ```
2066
+ */
2067
+ function useMark(config) {
2068
+ const { editor: providedEditor, type, hideWhenUnavailable = false, onToggled } = config;
2069
+ const { editor } = useTiptapEditor(providedEditor);
2070
+ const [isVisible, setIsVisible] = (0, react.useState)(true);
2071
+ const canToggle = canToggleMark(editor, type);
2072
+ const isActive = isMarkActive(editor, type);
2073
+ (0, react.useEffect)(() => {
2074
+ if (!editor) return;
2075
+ const handleSelectionUpdate = () => {
2076
+ setIsVisible(shouldShowButton$2({
2077
+ editor,
2078
+ type,
2079
+ hideWhenUnavailable
2080
+ }));
2081
+ };
2082
+ handleSelectionUpdate();
2083
+ editor.on("selectionUpdate", handleSelectionUpdate);
2084
+ return () => {
2085
+ editor.off("selectionUpdate", handleSelectionUpdate);
2086
+ };
2087
+ }, [
2088
+ editor,
2089
+ type,
2090
+ hideWhenUnavailable
2091
+ ]);
2092
+ return {
2093
+ isVisible,
2094
+ isActive,
2095
+ handleMark: (0, react.useCallback)(() => {
2096
+ if (!editor) return false;
2097
+ const success = toggleMark(editor, type);
2098
+ if (success) onToggled?.();
2099
+ return success;
2100
+ }, [
2101
+ editor,
2102
+ type,
2103
+ onToggled
2104
+ ]),
2105
+ canToggle,
2106
+ label: getFormattedMarkName(type),
2107
+ shortcutKeys: MARK_SHORTCUT_KEYS[type],
2108
+ Icon: markIcons[type]
2109
+ };
2110
+ }
2111
+ //#endregion
2112
+ //#region src/tiptap/components/ui/mark-button/mark-button.tsx
2113
+ function MarkShortcutBadge({ type, shortcutKeys = MARK_SHORTCUT_KEYS[type] }) {
2114
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material_Badge.default, { children: parseShortcutKeys({ shortcutKeys }) });
2115
+ }
2116
+ /**
2117
+ * Button component for toggling marks in a Tiptap editor.
2118
+ *
2119
+ * For custom button implementations, use the `useMark` hook instead.
2120
+ */
2121
+ var MarkButton = (0, react.forwardRef)(({ editor: providedEditor, type, text, hideWhenUnavailable = false, onToggled, showShortcut = false, onClick, children, ...buttonProps }, ref) => {
2122
+ const { editor } = useTiptapEditor(providedEditor);
2123
+ const { isVisible, handleMark, label, canToggle, isActive, Icon, shortcutKeys } = useMark({
2124
+ editor,
2125
+ type,
2126
+ hideWhenUnavailable,
2127
+ onToggled
2128
+ });
2129
+ const handleClick = (0, react.useCallback)((event) => {
2130
+ onClick?.(event, !isActive);
2131
+ if (event.defaultPrevented) return;
2132
+ handleMark();
2133
+ }, [handleMark, onClick]);
2134
+ if (!isVisible) return null;
2135
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
2136
+ value: type,
2137
+ type: "button",
2138
+ disabled: !canToggle,
2139
+ selected: isActive,
2140
+ "data-disabled": !canToggle,
2141
+ role: "button",
2142
+ tabIndex: -1,
2143
+ "aria-label": label,
2144
+ tooltip: label,
2145
+ onClick: handleClick,
2146
+ ...buttonProps,
2147
+ ref,
2148
+ children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2149
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { fontSize: "small" }),
2150
+ text && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2151
+ className: "tiptap-button-text",
2152
+ children: text
2153
+ }),
2154
+ showShortcut && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkShortcutBadge, {
2155
+ type,
2156
+ shortcutKeys
2157
+ })
2158
+ ] })
2159
+ });
2160
+ });
2161
+ MarkButton.displayName = "MarkButton";
2162
+ //#endregion
2163
+ //#region ../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.2._02cb1dd638d2ccdea7d16c50ea4ea174/node_modules/@mui/icons-material/FormatAlignCenterRounded.mjs
2164
+ var FormatAlignCenterRounded_default = (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M7 16c0 .55.45 1 1 1h8c.55 0 1-.45 1-1s-.45-1-1-1H8c-.55 0-1 .45-1 1m-3 5h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1s.45 1 1 1m0-8h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1s.45 1 1 1m3-5c0 .55.45 1 1 1h8c.55 0 1-.45 1-1s-.45-1-1-1H8c-.55 0-1 .45-1 1M3 4c0 .55.45 1 1 1h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1" }), "FormatAlignCenterRounded");
2165
+ //#endregion
2166
+ //#region ../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.2._02cb1dd638d2ccdea7d16c50ea4ea174/node_modules/@mui/icons-material/FormatAlignJustifyRounded.mjs
2167
+ var FormatAlignJustifyRounded_default = (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M4 21h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1s.45 1 1 1m0-4h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1s.45 1 1 1m0-4h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1s.45 1 1 1m0-4h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1s.45 1 1 1M3 4c0 .55.45 1 1 1h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1" }), "FormatAlignJustifyRounded");
2168
+ //#endregion
2169
+ //#region ../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.2._02cb1dd638d2ccdea7d16c50ea4ea174/node_modules/@mui/icons-material/FormatAlignLeftRounded.mjs
2170
+ var FormatAlignLeftRounded_default = (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M14 15H4c-.55 0-1 .45-1 1s.45 1 1 1h10c.55 0 1-.45 1-1s-.45-1-1-1m0-8H4c-.55 0-1 .45-1 1s.45 1 1 1h10c.55 0 1-.45 1-1s-.45-1-1-1M4 13h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1s.45 1 1 1m0 8h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1s.45 1 1 1M3 4c0 .55.45 1 1 1h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1" }), "FormatAlignLeftRounded");
2171
+ //#endregion
2172
+ //#region ../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.2._02cb1dd638d2ccdea7d16c50ea4ea174/node_modules/@mui/icons-material/FormatAlignRightRounded.mjs
2173
+ var FormatAlignRightRounded_default = (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M4 21h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1s.45 1 1 1m6-4h10c.55 0 1-.45 1-1s-.45-1-1-1H10c-.55 0-1 .45-1 1s.45 1 1 1m-6-4h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1s.45 1 1 1m6-4h10c.55 0 1-.45 1-1s-.45-1-1-1H10c-.55 0-1 .45-1 1s.45 1 1 1M3 4c0 .55.45 1 1 1h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1" }), "FormatAlignRightRounded");
2174
+ //#endregion
2175
+ //#region src/tiptap/components/ui/text-align-button/use-text-align.ts
2176
+ var TEXT_ALIGN_SHORTCUT_KEYS = {
2177
+ left: "mod+shift+l",
2178
+ center: "mod+shift+e",
2179
+ right: "mod+shift+r",
2180
+ justify: "mod+shift+j"
2181
+ };
2182
+ var textAlignIcons = {
2183
+ left: FormatAlignLeftRounded_default,
2184
+ center: FormatAlignCenterRounded_default,
2185
+ right: FormatAlignRightRounded_default,
2186
+ justify: FormatAlignJustifyRounded_default
2187
+ };
2188
+ var textAlignLabels = {
2189
+ left: "Aligner à gauche",
2190
+ center: "Aligner au centre",
2191
+ right: "Aligner à droite",
2192
+ justify: "Justifier"
2193
+ };
2194
+ /**
2195
+ * Checks if text alignment can be performed in the current editor state
2196
+ */
2197
+ function canSetTextAlign(editor, align) {
2198
+ if (!editor || editor.isDestroyed || !editor.isEditable) return false;
2199
+ if (!isExtensionAvailable(editor, "textAlign") || isNodeTypeSelected(editor, ["image", "horizontalRule"])) return false;
2200
+ try {
2201
+ return editor.can().setTextAlign(align);
2202
+ } catch {
2203
+ return false;
2204
+ }
2205
+ }
2206
+ function hasSetTextAlign(commands) {
2207
+ return "setTextAlign" in commands;
2208
+ }
2209
+ /**
2210
+ * Checks if the text alignment is currently active
2211
+ */
2212
+ function isTextAlignActive(editor, align) {
2213
+ if (!editor || !editor.isEditable) return false;
2214
+ return editor.isActive({ textAlign: align });
2215
+ }
2216
+ /**
2217
+ * Sets text alignment in the editor
2218
+ */
2219
+ function setTextAlign(editor, align) {
2220
+ if (!editor || editor.isDestroyed || !editor.isEditable) return false;
2221
+ if (!canSetTextAlign(editor, align)) return false;
2222
+ try {
2223
+ const chain = editor.chain().focus();
2224
+ if (hasSetTextAlign(chain)) return chain.setTextAlign(align).run();
2225
+ } catch {
2226
+ return false;
2227
+ }
2228
+ return false;
2229
+ }
2230
+ /**
2231
+ * Determines if the text align button should be shown
2232
+ */
2233
+ function shouldShowButton$1(props) {
2234
+ const { editor, hideWhenUnavailable, align } = props;
2235
+ if (!editor) return false;
2236
+ if (!hideWhenUnavailable) return true;
2237
+ if (!editor.isEditable) return false;
2238
+ if (!isExtensionAvailable(editor, "textAlign")) return false;
2239
+ if (!editor.isActive("code")) return canSetTextAlign(editor, align);
2240
+ return true;
2241
+ }
2242
+ /**
2243
+ * Custom hook that provides text align functionality for Tiptap editor
2244
+ *
2245
+ * @example
2246
+ * ```tsx
2247
+ * // Simple usage
2248
+ * function MySimpleAlignButton() {
2249
+ * const { isVisible, handleTextAlign } = useTextAlign({ align: "center" })
2250
+ *
2251
+ * if (!isVisible) return null
2252
+ *
2253
+ * return <button onClick={handleTextAlign}>Align Center</button>
2254
+ * }
2255
+ *
2256
+ * // Advanced usage with configuration
2257
+ * function MyAdvancedAlignButton() {
2258
+ * const { isVisible, handleTextAlign, label, isActive } = useTextAlign({
2259
+ * editor: myEditor,
2260
+ * align: "right",
2261
+ * hideWhenUnavailable: true,
2262
+ * onAligned: () => console.log('Text aligned!')
2263
+ * })
2264
+ *
2265
+ * if (!isVisible) return null
2266
+ *
2267
+ * return (
2268
+ * <MyButton
2269
+ * onClick={handleTextAlign}
2270
+ * aria-pressed={isActive}
2271
+ * aria-label={label}
2272
+ * >
2273
+ * Align Right
2274
+ * </MyButton>
2275
+ * )
2276
+ * }
2277
+ * ```
2278
+ */
2279
+ function useTextAlign(config) {
2280
+ const { editor: providedEditor, align, hideWhenUnavailable = false, onAligned } = config;
2281
+ const { editor } = useTiptapEditor(providedEditor);
2282
+ const [isVisible, setIsVisible] = (0, react.useState)(true);
2283
+ const canAlign = canSetTextAlign(editor, align);
2284
+ const isActive = isTextAlignActive(editor, align);
2285
+ (0, react.useEffect)(() => {
2286
+ if (!editor) return;
2287
+ const handleSelectionUpdate = () => {
2288
+ setIsVisible(shouldShowButton$1({
2289
+ editor,
2290
+ align,
2291
+ hideWhenUnavailable
2292
+ }));
2293
+ };
2294
+ handleSelectionUpdate();
2295
+ editor.on("selectionUpdate", handleSelectionUpdate);
2296
+ return () => {
2297
+ editor.off("selectionUpdate", handleSelectionUpdate);
2298
+ };
2299
+ }, [
2300
+ editor,
2301
+ hideWhenUnavailable,
2302
+ align
2303
+ ]);
2304
+ return {
2305
+ isVisible,
2306
+ isActive,
2307
+ handleTextAlign: (0, react.useCallback)(() => {
2308
+ if (!editor) return false;
2309
+ const success = setTextAlign(editor, align);
2310
+ if (success) onAligned?.();
2311
+ return success;
2312
+ }, [
2313
+ editor,
2314
+ align,
2315
+ onAligned
2316
+ ]),
2317
+ canAlign,
2318
+ label: textAlignLabels[align],
2319
+ shortcutKeys: TEXT_ALIGN_SHORTCUT_KEYS[align],
2320
+ Icon: textAlignIcons[align]
2321
+ };
2322
+ }
2323
+ //#endregion
2324
+ //#region src/tiptap/components/ui/text-align-button/text-align-button.tsx
2325
+ function TextAlignShortcutBadge({ align, shortcutKeys = TEXT_ALIGN_SHORTCUT_KEYS[align] }) {
2326
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material_Badge.default, { children: parseShortcutKeys({ shortcutKeys }) });
2327
+ }
2328
+ /**
2329
+ * Button component for setting text alignment in a Tiptap editor.
2330
+ *
2331
+ * For custom button implementations, use the `useTextAlign` hook instead.
2332
+ */
2333
+ var TextAlignButton = (0, react.forwardRef)(({ editor: providedEditor, align, text, hideWhenUnavailable = false, onAligned, showShortcut = false, onClick, icon: CustomIcon, children, ...buttonProps }, ref) => {
2334
+ const { editor } = useTiptapEditor(providedEditor);
2335
+ const { isVisible, handleTextAlign, label, canAlign, isActive, Icon, shortcutKeys } = useTextAlign({
2336
+ editor,
2337
+ align,
2338
+ hideWhenUnavailable,
2339
+ onAligned
2340
+ });
2341
+ const handleClick = (0, react.useCallback)((event) => {
2342
+ onClick?.(event, !isActive);
2343
+ if (event.defaultPrevented) return;
2344
+ handleTextAlign();
2345
+ }, [handleTextAlign, onClick]);
2346
+ if (!isVisible) return null;
2347
+ const RenderIcon = CustomIcon ?? Icon;
2348
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
2349
+ value: align,
2350
+ type: "button",
2351
+ disabled: !canAlign,
2352
+ selected: isActive,
2353
+ "data-disabled": !canAlign,
2354
+ role: "button",
2355
+ tabIndex: -1,
2356
+ "aria-label": label,
2357
+ tooltip: label,
2358
+ onClick: handleClick,
2359
+ ...buttonProps,
2360
+ ref,
2361
+ children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2362
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderIcon, { fontSize: "small" }),
2363
+ text && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2364
+ className: "tiptap-button-text",
2365
+ children: text
2366
+ }),
2367
+ showShortcut && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TextAlignShortcutBadge, {
2368
+ align,
2369
+ shortcutKeys
2370
+ })
2371
+ ] })
2372
+ });
2373
+ });
2374
+ TextAlignButton.displayName = "TextAlignButton";
2375
+ //#endregion
2376
+ //#region ../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.2._02cb1dd638d2ccdea7d16c50ea4ea174/node_modules/@mui/icons-material/RedoRounded.mjs
2377
+ var RedoRounded_default = (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M18.4 10.6C16.55 8.99 14.15 8 11.5 8c-4.16 0-7.74 2.42-9.44 5.93-.32.67.04 1.47.75 1.71.59.2 1.23-.08 1.5-.64 1.3-2.66 4.03-4.5 7.19-4.5 1.95 0 3.73.72 5.12 1.88l-1.91 1.91c-.63.63-.19 1.71.7 1.71H21c.55 0 1-.45 1-1V9.41c0-.89-1.08-1.34-1.71-.71z" }), "RedoRounded");
2378
+ //#endregion
2379
+ //#region ../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.2._02cb1dd638d2ccdea7d16c50ea4ea174/node_modules/@mui/icons-material/UndoRounded.mjs
2380
+ var UndoRounded_default = (0, _mui_material_SvgIcon.createSvgIcon)(/*#__PURE__*/ (0, react_jsx_runtime.jsx)("path", { d: "M12.5 8c-2.65 0-5.05.99-6.9 2.6L3.71 8.71C3.08 8.08 2 8.52 2 9.41V15c0 .55.45 1 1 1h5.59c.89 0 1.34-1.08.71-1.71l-1.91-1.91c1.39-1.16 3.16-1.88 5.12-1.88 3.16 0 5.89 1.84 7.19 4.5.27.56.91.84 1.5.64.71-.23 1.07-1.04.75-1.72C20.23 10.42 16.65 8 12.5 8" }), "UndoRounded");
2381
+ //#endregion
2382
+ //#region src/tiptap/components/ui/undo-redo-button/use-undo-redo.ts
2383
+ var UNDO_REDO_SHORTCUT_KEYS = {
2384
+ undo: "mod+z",
2385
+ redo: "mod+shift+z"
2386
+ };
2387
+ var historyActionLabels = {
2388
+ undo: "Annuler",
2389
+ redo: "Rétablir"
2390
+ };
2391
+ var historyIcons = {
2392
+ undo: UndoRounded_default,
2393
+ redo: RedoRounded_default
2394
+ };
2395
+ /**
2396
+ * Checks if a history action can be executed
2397
+ */
2398
+ function canExecuteUndoRedoAction(editor, action) {
2399
+ if (!editor || editor.isDestroyed || !editor.isEditable) return false;
2400
+ try {
2401
+ if (isNodeTypeSelected(editor, ["image"])) return false;
2402
+ const commands = editor.can();
2403
+ const command = action === "undo" ? commands.undo : commands.redo;
2404
+ return typeof command === "function" ? command() : false;
2405
+ } catch {
2406
+ return false;
2407
+ }
2408
+ }
2409
+ /**
2410
+ * Executes a history action on the editor
2411
+ */
2412
+ function executeUndoRedoAction(editor, action) {
2413
+ if (!editor || editor.isDestroyed || !editor.isEditable) return false;
2414
+ if (!canExecuteUndoRedoAction(editor, action)) return false;
2415
+ try {
2416
+ const chain = editor.chain().focus();
2417
+ return action === "undo" ? chain.undo().run() : chain.redo().run();
2418
+ } catch {
2419
+ return false;
2420
+ }
2421
+ }
2422
+ /**
2423
+ * Determines if the history button should be shown
2424
+ */
2425
+ function shouldShowButton(props) {
2426
+ const { editor, hideWhenUnavailable, action } = props;
2427
+ if (!editor) return false;
2428
+ if (!hideWhenUnavailable) return true;
2429
+ if (!editor.isEditable) return false;
2430
+ if (!editor.isActive("code")) return canExecuteUndoRedoAction(editor, action);
2431
+ return true;
2432
+ }
2433
+ /**
2434
+ * Custom hook that provides history functionality for Tiptap editor
2435
+ *
2436
+ * @example
2437
+ * ```tsx
2438
+ * // Simple usage
2439
+ * function MySimpleUndoButton() {
2440
+ * const { isVisible, handleAction } = useHistory({ action: "undo" })
2441
+ *
2442
+ * if (!isVisible) return null
2443
+ *
2444
+ * return <button onClick={handleAction}>Undo</button>
2445
+ * }
2446
+ *
2447
+ * // Advanced usage with configuration
2448
+ * function MyAdvancedRedoButton() {
2449
+ * const { isVisible, handleAction, label } = useHistory({
2450
+ * editor: myEditor,
2451
+ * action: "redo",
2452
+ * hideWhenUnavailable: true,
2453
+ * onExecuted: () => console.log('Action executed!')
2454
+ * })
2455
+ *
2456
+ * if (!isVisible) return null
2457
+ *
2458
+ * return (
2459
+ * <MyButton
2460
+ * onClick={handleAction}
2461
+ * aria-label={label}
2462
+ * >
2463
+ * Redo
2464
+ * </MyButton>
2465
+ * )
2466
+ * }
2467
+ * ```
2468
+ */
2469
+ function useUndoRedo(config) {
2470
+ const { editor: providedEditor, action, hideWhenUnavailable = false, onExecuted } = config;
2471
+ const { editor } = useTiptapEditor(providedEditor);
2472
+ const [isVisible, setIsVisible] = (0, react.useState)(true);
2473
+ const canExecute = canExecuteUndoRedoAction(editor, action);
2474
+ (0, react.useEffect)(() => {
2475
+ if (!editor) return;
2476
+ const handleUpdate = () => {
2477
+ setIsVisible(shouldShowButton({
2478
+ editor,
2479
+ hideWhenUnavailable,
2480
+ action
2481
+ }));
2482
+ };
2483
+ handleUpdate();
2484
+ editor.on("transaction", handleUpdate);
2485
+ return () => {
2486
+ editor.off("transaction", handleUpdate);
2487
+ };
2488
+ }, [
2489
+ editor,
2490
+ hideWhenUnavailable,
2491
+ action
2492
+ ]);
2493
+ return {
2494
+ isVisible,
2495
+ handleAction: (0, react.useCallback)(() => {
2496
+ if (!editor) return false;
2497
+ const success = executeUndoRedoAction(editor, action);
2498
+ if (success) onExecuted?.();
2499
+ return success;
2500
+ }, [
2501
+ editor,
2502
+ action,
2503
+ onExecuted
2504
+ ]),
2505
+ canExecute,
2506
+ label: historyActionLabels[action],
2507
+ shortcutKeys: UNDO_REDO_SHORTCUT_KEYS[action],
2508
+ Icon: historyIcons[action]
2509
+ };
2510
+ }
2511
+ //#endregion
2512
+ //#region src/tiptap/components/ui/undo-redo-button/undo-redo-button.tsx
2513
+ function HistoryShortcutBadge({ action, shortcutKeys = UNDO_REDO_SHORTCUT_KEYS[action] }) {
2514
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material_Badge.default, { children: parseShortcutKeys({ shortcutKeys }) });
2515
+ }
2516
+ /**
2517
+ * Button component for triggering undo/redo actions in a Tiptap editor.
2518
+ *
2519
+ * For custom button implementations, use the `useHistory` hook instead.
2520
+ */
2521
+ var UndoRedoButton = (0, react.forwardRef)(({ editor: providedEditor, action, text, hideWhenUnavailable = false, onExecuted, showShortcut = false, onClick, children, ...buttonProps }, ref) => {
2522
+ const { editor } = useTiptapEditor(providedEditor);
2523
+ const { isVisible, handleAction, label, canExecute, Icon, shortcutKeys } = useUndoRedo({
2524
+ editor,
2525
+ action,
2526
+ hideWhenUnavailable,
2527
+ onExecuted
2528
+ });
2529
+ const handleClick = (0, react.useCallback)((event) => {
2530
+ onClick?.(event, true);
2531
+ if (event.defaultPrevented) return;
2532
+ handleAction();
2533
+ }, [handleAction, onClick]);
2534
+ if (!isVisible) return null;
2535
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
2536
+ value: action,
2537
+ type: "button",
2538
+ disabled: !canExecute,
2539
+ "data-disabled": !canExecute,
2540
+ role: "button",
2541
+ tabIndex: -1,
2542
+ "aria-label": label,
2543
+ tooltip: label,
2544
+ onClick: handleClick,
2545
+ ...buttonProps,
2546
+ ref,
2547
+ children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2548
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { fontSize: "small" }),
2549
+ text && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2550
+ className: "tiptap-button-text",
2551
+ children: text
2552
+ }),
2553
+ showShortcut && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HistoryShortcutBadge, {
2554
+ action,
2555
+ shortcutKeys
2556
+ })
2557
+ ] })
2558
+ });
2559
+ });
2560
+ UndoRedoButton.displayName = "UndoRedoButton";
2561
+ //#endregion
2562
+ //#region src/tiptap/hooks/use-composed-ref.ts
2563
+ var updateRef = (ref, value) => {
2564
+ if (typeof ref === "function") ref(value);
2565
+ else if (ref && typeof ref === "object" && "current" in ref) {
2566
+ const mutableRef = ref;
2567
+ mutableRef.current = value;
2568
+ }
2569
+ };
2570
+ var useComposedRef = (libRef, userRef) => {
2571
+ const prevUserRef = (0, react.useRef)(null);
2572
+ return (0, react.useCallback)((instance) => {
2573
+ if (libRef && "current" in libRef) {
2574
+ const mutableLibRef = libRef;
2575
+ mutableLibRef.current = instance;
2576
+ }
2577
+ if (prevUserRef.current) updateRef(prevUserRef.current, null);
2578
+ prevUserRef.current = userRef;
2579
+ if (userRef) updateRef(userRef, instance);
2580
+ }, [libRef, userRef]);
2581
+ };
2582
+ //#endregion
2583
+ //#region src/tiptap/hooks/use-menu-navigation.ts
2584
+ /**
2585
+ * Hook that implements keyboard navigation for dropdown menus and command palettes.
2586
+ *
2587
+ * Handles arrow keys, tab, home/end, enter for selection, and escape to close.
2588
+ * Works with both Tiptap editors and regular DOM elements.
2589
+ *
2590
+ * @param options - Configuration options for the menu navigation
2591
+ * @returns Object containing the selected index and a setter function
2592
+ */
2593
+ function useMenuNavigation({ editor, containerRef, query, items, onSelect, onClose, orientation = "vertical", autoSelectFirstItem = true, loopOnTab = true }) {
2594
+ const [selectedIndex, setSelectedIndex] = (0, react.useState)(autoSelectFirstItem ? 0 : -1);
2595
+ (0, react.useEffect)(() => {
2596
+ const handleKeyboardNavigation = (event) => {
2597
+ if (!items.length) return false;
2598
+ const moveNext = () => setSelectedIndex((currentIndex) => {
2599
+ if (currentIndex === -1) return 0;
2600
+ return (currentIndex + 1) % items.length;
2601
+ });
2602
+ const movePrev = () => setSelectedIndex((currentIndex) => {
2603
+ if (currentIndex === -1) return items.length - 1;
2604
+ return (currentIndex - 1 + items.length) % items.length;
2605
+ });
2606
+ switch (event.key) {
2607
+ case "ArrowUp":
2608
+ if (orientation === "horizontal") return false;
2609
+ event.preventDefault();
2610
+ movePrev();
2611
+ return true;
2612
+ case "ArrowDown":
2613
+ if (orientation === "horizontal") return false;
2614
+ event.preventDefault();
2615
+ moveNext();
2616
+ return true;
2617
+ case "ArrowLeft":
2618
+ if (orientation === "vertical") return false;
2619
+ event.preventDefault();
2620
+ movePrev();
2621
+ return true;
2622
+ case "ArrowRight":
2623
+ if (orientation === "vertical") return false;
2624
+ event.preventDefault();
2625
+ moveNext();
2626
+ return true;
2627
+ case "Tab":
2628
+ if (!loopOnTab) return false;
2629
+ event.preventDefault();
2630
+ if (event.shiftKey) movePrev();
2631
+ else moveNext();
2632
+ return true;
2633
+ case "Home":
2634
+ event.preventDefault();
2635
+ setSelectedIndex(0);
2636
+ return true;
2637
+ case "End":
2638
+ event.preventDefault();
2639
+ setSelectedIndex(items.length - 1);
2640
+ return true;
2641
+ case "Enter":
2642
+ if (event.isComposing) return false;
2643
+ event.preventDefault();
2644
+ if (selectedIndex !== -1 && items[selectedIndex]) onSelect?.(items[selectedIndex]);
2645
+ return true;
2646
+ case "Escape":
2647
+ if (!onClose) return false;
2648
+ event.preventDefault();
2649
+ onClose?.();
2650
+ return true;
2651
+ default: return false;
2652
+ }
2653
+ };
2654
+ let targetElement = null;
2655
+ if (editor) targetElement = editor.view.dom;
2656
+ else if (containerRef?.current) targetElement = containerRef.current;
2657
+ if (targetElement) {
2658
+ targetElement.addEventListener("keydown", handleKeyboardNavigation, true);
2659
+ return () => {
2660
+ targetElement?.removeEventListener("keydown", handleKeyboardNavigation, true);
2661
+ };
2662
+ }
2663
+ }, [
2664
+ editor,
2665
+ containerRef,
2666
+ items,
2667
+ selectedIndex,
2668
+ onSelect,
2669
+ onClose,
2670
+ orientation,
2671
+ loopOnTab
2672
+ ]);
2673
+ (0, react.useEffect)(() => {
2674
+ if (query) setSelectedIndex(autoSelectFirstItem ? 0 : -1);
2675
+ }, [query, autoSelectFirstItem]);
2676
+ return {
2677
+ selectedIndex: items.length ? selectedIndex : void 0,
2678
+ setSelectedIndex
2679
+ };
2680
+ }
2681
+ //#endregion
2682
+ //#region src/tiptap/components/ui-primitive/toolbar/toolbar.tsx
2683
+ var useToolbarNavigation = (toolbarRef) => {
2684
+ const [items, setItems] = (0, react.useState)([]);
2685
+ const initialContainerTabIndexRef = (0, react.useRef)(void 0);
2686
+ const collectItems = (0, react.useCallback)(() => {
2687
+ if (!toolbarRef.current) return [];
2688
+ return Array.from(toolbarRef.current.querySelectorAll("button:not([disabled]), [role=\"button\"]:not([disabled]), [tabindex=\"0\"]:not([disabled])"));
2689
+ }, [toolbarRef]);
2690
+ (0, react.useEffect)(() => {
2691
+ const toolbar = toolbarRef.current;
2692
+ if (!toolbar) return;
2693
+ if (initialContainerTabIndexRef.current === void 0) initialContainerTabIndexRef.current = toolbar.hasAttribute("tabindex") && toolbar.tabIndex >= 0 ? toolbar.tabIndex : null;
2694
+ const updateItems = () => setItems(collectItems());
2695
+ updateItems();
2696
+ const observer = new MutationObserver(updateItems);
2697
+ observer.observe(toolbar, {
2698
+ childList: true,
2699
+ subtree: true,
2700
+ attributes: true,
2701
+ attributeFilter: ["disabled"]
2702
+ });
2703
+ return () => observer.disconnect();
2704
+ }, [collectItems, toolbarRef]);
2705
+ const { selectedIndex, setSelectedIndex } = useMenuNavigation({
2706
+ containerRef: toolbarRef,
2707
+ items,
2708
+ orientation: "horizontal",
2709
+ onSelect: (el) => el.click(),
2710
+ autoSelectFirstItem: false,
2711
+ loopOnTab: false
2712
+ });
2713
+ (0, react.useEffect)(() => {
2714
+ const toolbar = toolbarRef.current;
2715
+ if (!toolbar) return;
2716
+ const handleFocus = (e) => {
2717
+ const target = e.target;
2718
+ if (toolbar.contains(target)) {
2719
+ target.setAttribute("data-focus-visible", "true");
2720
+ const itemIndex = items.indexOf(target);
2721
+ if (itemIndex >= 0) setSelectedIndex(itemIndex);
2722
+ }
2723
+ };
2724
+ const handleBlur = (e) => {
2725
+ const target = e.target;
2726
+ if (toolbar.contains(target)) target.removeAttribute("data-focus-visible");
2727
+ const nextTarget = e.relatedTarget;
2728
+ const NodeConstructor = toolbar.ownerDocument.defaultView?.Node;
2729
+ if ((!nextTarget || !NodeConstructor || !(nextTarget instanceof NodeConstructor) || !toolbar.contains(nextTarget)) && initialContainerTabIndexRef.current != null) queueMicrotask(() => {
2730
+ if (toolbar.isConnected && !toolbar.contains(toolbar.ownerDocument.activeElement)) {
2731
+ toolbar.tabIndex = initialContainerTabIndexRef.current;
2732
+ setSelectedIndex(-1);
2733
+ }
2734
+ });
2735
+ };
2736
+ toolbar.addEventListener("focus", handleFocus, true);
2737
+ toolbar.addEventListener("blur", handleBlur, true);
2738
+ return () => {
2739
+ toolbar.removeEventListener("focus", handleFocus, true);
2740
+ toolbar.removeEventListener("blur", handleBlur, true);
2741
+ };
2742
+ }, [
2743
+ items,
2744
+ setSelectedIndex,
2745
+ toolbarRef
2746
+ ]);
2747
+ (0, react.useEffect)(() => {
2748
+ if (!items.length) return;
2749
+ const toolbar = toolbarRef.current;
2750
+ if (!toolbar) return;
2751
+ const activeEl = toolbar.ownerDocument.activeElement;
2752
+ if (activeEl && !toolbar.contains(activeEl)) return;
2753
+ const initialContainerTabIndex = initialContainerTabIndexRef.current;
2754
+ const currentIndex = selectedIndex ?? -1;
2755
+ if (initialContainerTabIndex != null) {
2756
+ items.forEach((item) => {
2757
+ item.tabIndex = -1;
2758
+ });
2759
+ if (currentIndex >= 0 && items[currentIndex]) {
2760
+ toolbar.tabIndex = -1;
2761
+ items[currentIndex].focus();
2762
+ } else toolbar.tabIndex = initialContainerTabIndex;
2763
+ return;
2764
+ }
2765
+ const existingTabStop = items.findIndex((item) => item.tabIndex === 0);
2766
+ const activeIndex = currentIndex >= 0 && items[currentIndex] ? currentIndex : existingTabStop >= 0 ? existingTabStop : 0;
2767
+ items.forEach((item, index) => {
2768
+ item.tabIndex = index === activeIndex ? 0 : -1;
2769
+ });
2770
+ if (currentIndex >= 0) items[activeIndex]?.focus();
2771
+ }, [
2772
+ selectedIndex,
2773
+ items,
2774
+ toolbarRef
2775
+ ]);
2776
+ };
2777
+ var Toolbar = (0, react.forwardRef)(({ children, className, variant = "fixed", ...props }, ref) => {
2778
+ const toolbarRef = (0, react.useRef)(null);
2779
+ const composedRef = useComposedRef(toolbarRef, ref);
2780
+ useToolbarNavigation(toolbarRef);
2781
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2782
+ ref: composedRef,
2783
+ role: "toolbar",
2784
+ "aria-label": "toolbar",
2785
+ "data-variant": variant,
2786
+ className: cn("tiptap-toolbar", className),
2787
+ ...props,
2788
+ children
2789
+ });
2790
+ });
2791
+ Toolbar.displayName = "Toolbar";
2792
+ var ToolbarGroup = (0, react.forwardRef)(({ children, className, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2793
+ ref,
2794
+ role: "group",
2795
+ className: cn("tiptap-toolbar-group", className),
2796
+ ...props,
2797
+ children
2798
+ }));
2799
+ ToolbarGroup.displayName = "ToolbarGroup";
2800
+ var ToolbarSeparator = () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material_Divider.default, {
2801
+ orientation: "vertical",
2802
+ flexItem: true,
2803
+ sx: { my: 1 }
2804
+ });
2805
+ ToolbarSeparator.displayName = "ToolbarSeparator";
2806
+ //#endregion
2807
+ //#region src/tiptap/components/ui-primitive/formatting-controls/formatting-controls.tsx
2808
+ var FormattingControls = ({ editor }) => {
2809
+ const hasSuperscriptSubscript = editor.extensionManager.extensions.map((e) => e.name).some((name) => name === "subscript" || name === "superscript");
2810
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2811
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ToolbarGroup, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UndoRedoButton, { action: "undo" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UndoRedoButton, { action: "redo" })] }),
2812
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarSeparator, {}),
2813
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ToolbarGroup, { children: [
2814
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeadingMenu, {
2815
+ modal: false,
2816
+ levels: [
2817
+ 1,
2818
+ 2,
2819
+ 3,
2820
+ 4
2821
+ ]
2822
+ }),
2823
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ListButton, { type: "bulletList" }),
2824
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ListButton, { type: "orderedList" }),
2825
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockquoteButton, {}),
2826
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodeBlockButton, {})
2827
+ ] }),
2828
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarSeparator, {}),
2829
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ToolbarGroup, { children: [
2830
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkButton, { type: "bold" }),
2831
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkButton, { type: "italic" }),
2832
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkButton, { type: "strike" }),
2833
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkButton, { type: "code" }),
2834
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkButton, { type: "underline" }),
2835
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LinkPopover, {})
2836
+ ] }),
2837
+ hasSuperscriptSubscript ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarSeparator, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ToolbarGroup, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkButton, { type: "superscript" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkButton, { type: "subscript" })] })] }) : null,
2838
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarSeparator, {}),
2839
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ToolbarGroup, { children: [
2840
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TextAlignButton, { align: "left" }),
2841
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TextAlignButton, { align: "center" }),
2842
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TextAlignButton, { align: "right" }),
2843
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TextAlignButton, { align: "justify" })
2844
+ ] })
2845
+ ] });
2846
+ };
2847
+ FormattingControls.displayName = "FormattingControls";
2848
+ //#endregion
2849
+ //#region src/tiptap/components/ui-primitive/spacer/spacer.tsx
2850
+ function Spacer({ orientation = "horizontal", size, style = {}, ...props }) {
2851
+ const computedStyle = {
2852
+ ...style,
2853
+ ...orientation === "horizontal" && !size && { flex: 1 },
2854
+ ...size && {
2855
+ width: orientation === "vertical" ? "1px" : size,
2856
+ height: orientation === "horizontal" ? "1px" : size
2857
+ }
2858
+ };
2859
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2860
+ ...props,
2861
+ style: computedStyle
2862
+ });
2863
+ }
2864
+ //#endregion
2865
+ exports.FormattingControls = FormattingControls;
2866
+ exports.Spacer = Spacer;
2867
+ exports.Toolbar = Toolbar;
2868
+ exports.ToolbarGroup = ToolbarGroup;
2869
+ exports.ToolbarSeparator = ToolbarSeparator;
2870
+
2871
+ //# sourceMappingURL=index.cjs.js.map