@projectfive/v-richtext-vue2 0.0.0-stage → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/Readme.md +69 -0
  2. package/dist/style.css +1 -0
  3. package/dist/types/richtext-vue/src/components/input-richtext/CharacterCount.vue.d.ts +19 -0
  4. package/dist/types/richtext-vue/src/components/input-richtext/InputRichtext.vue.d.ts +135 -0
  5. package/dist/types/richtext-vue/src/components/input-richtext/MediaControls.vue.d.ts +3 -0
  6. package/dist/types/richtext-vue/src/components/input-richtext/MentionMenu.vue.d.ts +21 -0
  7. package/dist/types/richtext-vue/src/components/input-richtext/TableMenu.vue.d.ts +19 -0
  8. package/dist/types/richtext-vue/src/components/input-richtext/TableOfContents.vue.d.ts +3 -0
  9. package/dist/types/richtext-vue/src/components/input-richtext/TemplatesMenu.vue.d.ts +18 -0
  10. package/dist/types/richtext-vue/src/components/input-richtext/UploadPopover.vue.d.ts +24 -0
  11. package/dist/types/richtext-vue/src/components/input-richtext/VisibilityConditionsMenu.vue.d.ts +20 -0
  12. package/dist/types/richtext-vue/src/components/input-richtext/YoutubePopover.vue.d.ts +4 -0
  13. package/dist/types/richtext-vue/src/components/input-richtext/extensions/file-drop.d.ts +22 -0
  14. package/dist/types/richtext-vue/src/components/input-richtext/extensions/file-node.d.ts +18 -0
  15. package/dist/types/richtext-vue/src/components/input-richtext/extensions/heading-id.d.ts +14 -0
  16. package/dist/types/richtext-vue/src/components/input-richtext/extensions/index.d.ts +36 -0
  17. package/dist/types/richtext-vue/src/components/input-richtext/extensions/link.d.ts +8 -0
  18. package/dist/types/richtext-vue/src/components/input-richtext/extensions/media-attributes.d.ts +8 -0
  19. package/dist/types/richtext-vue/src/components/input-richtext/extensions/mention-menu.d.ts +62 -0
  20. package/dist/types/richtext-vue/src/components/input-richtext/extensions/mention.d.ts +16 -0
  21. package/dist/types/richtext-vue/src/components/input-richtext/extensions/previews.d.ts +8 -0
  22. package/dist/types/richtext-vue/src/components/input-richtext/extensions/task-item.d.ts +8 -0
  23. package/dist/types/richtext-vue/src/components/input-richtext/extensions/visibility-conditions.d.ts +32 -0
  24. package/dist/types/richtext-vue/src/components/input-richtext/liquid.d.ts +11 -0
  25. package/dist/types/richtext-vue/src/components/input-richtext/types.d.ts +122 -0
  26. package/dist/types/richtext-vue/src/components/tiptap-icons/Icon.vue.d.ts +15 -0
  27. package/dist/types/richtext-vue/src/components/tiptap-icons/index.d.ts +40 -0
  28. package/dist/types/richtext-vue/src/components/tiptap-ui/CommandButton.vue.d.ts +36 -0
  29. package/dist/types/richtext-vue/src/components/tiptap-ui/blockquote-button/index.d.ts +1 -0
  30. package/dist/types/richtext-vue/src/components/tiptap-ui/blockquote-button/use-blockquote.d.ts +12 -0
  31. package/dist/types/richtext-vue/src/components/tiptap-ui/code-block-button/index.d.ts +1 -0
  32. package/dist/types/richtext-vue/src/components/tiptap-ui/code-block-button/use-code-block.d.ts +12 -0
  33. package/dist/types/richtext-vue/src/components/tiptap-ui/color-highlight-button/index.d.ts +1 -0
  34. package/dist/types/richtext-vue/src/components/tiptap-ui/color-highlight-button/use-color-highlight.d.ts +125 -0
  35. package/dist/types/richtext-vue/src/components/tiptap-ui/color-highlight-popover/ColorHighlightPopover.vue.d.ts +3 -0
  36. package/dist/types/richtext-vue/src/components/tiptap-ui/color-highlight-popover/ColorHighlightPopoverButton.vue.d.ts +17 -0
  37. package/dist/types/richtext-vue/src/components/tiptap-ui/color-highlight-popover/ColorHighlightPopoverContent.vue.d.ts +183 -0
  38. package/dist/types/richtext-vue/src/components/tiptap-ui/color-highlight-popover/index.d.ts +3 -0
  39. package/dist/types/richtext-vue/src/components/tiptap-ui/command.d.ts +26 -0
  40. package/dist/types/richtext-vue/src/components/tiptap-ui/heading-button/index.d.ts +1 -0
  41. package/dist/types/richtext-vue/src/components/tiptap-ui/heading-button/use-heading.d.ts +25 -0
  42. package/dist/types/richtext-vue/src/components/tiptap-ui/heading-dropdown-menu/HeadingDropdownMenu.vue.d.ts +32 -0
  43. package/dist/types/richtext-vue/src/components/tiptap-ui/heading-dropdown-menu/index.d.ts +2 -0
  44. package/dist/types/richtext-vue/src/components/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.d.ts +6 -0
  45. package/dist/types/richtext-vue/src/components/tiptap-ui/link-popover/LinkButton.vue.d.ts +17 -0
  46. package/dist/types/richtext-vue/src/components/tiptap-ui/link-popover/LinkContent.vue.d.ts +18 -0
  47. package/dist/types/richtext-vue/src/components/tiptap-ui/link-popover/LinkMain.vue.d.ts +28 -0
  48. package/dist/types/richtext-vue/src/components/tiptap-ui/link-popover/LinkPopover.vue.d.ts +62 -0
  49. package/dist/types/richtext-vue/src/components/tiptap-ui/link-popover/index.d.ts +4 -0
  50. package/dist/types/richtext-vue/src/components/tiptap-ui/link-popover/use-link-popover.d.ts +96 -0
  51. package/dist/types/richtext-vue/src/components/tiptap-ui/list-button/index.d.ts +1 -0
  52. package/dist/types/richtext-vue/src/components/tiptap-ui/list-button/use-list.d.ts +22 -0
  53. package/dist/types/richtext-vue/src/components/tiptap-ui/list-dropdown-menu/ListDropdownMenu.vue.d.ts +32 -0
  54. package/dist/types/richtext-vue/src/components/tiptap-ui/list-dropdown-menu/index.d.ts +2 -0
  55. package/dist/types/richtext-vue/src/components/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.d.ts +15 -0
  56. package/dist/types/richtext-vue/src/components/tiptap-ui/mark-button/index.d.ts +1 -0
  57. package/dist/types/richtext-vue/src/components/tiptap-ui/mark-button/use-mark.d.ts +29 -0
  58. package/dist/types/richtext-vue/src/components/tiptap-ui/text-align-button/index.d.ts +1 -0
  59. package/dist/types/richtext-vue/src/components/tiptap-ui/text-align-button/use-text-align.d.ts +27 -0
  60. package/dist/types/richtext-vue/src/components/tiptap-ui/undo-redo-button/index.d.ts +1 -0
  61. package/dist/types/richtext-vue/src/components/tiptap-ui/undo-redo-button/use-undo-redo.d.ts +17 -0
  62. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/button/Button.vue.d.ts +53 -0
  63. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/button/index.d.ts +1 -0
  64. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/button-group/ButtonGroup.vue.d.ts +43 -0
  65. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/button-group/index.d.ts +1 -0
  66. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/card/Card.vue.d.ts +16 -0
  67. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/card/CardBody.vue.d.ts +15 -0
  68. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/card/CardItemGroup.vue.d.ts +42 -0
  69. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/card/index.d.ts +3 -0
  70. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/dropdown-menu/DropdownMenu.vue.d.ts +66 -0
  71. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/dropdown-menu/index.d.ts +1 -0
  72. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/input/Input.vue.d.ts +20 -0
  73. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/input/index.d.ts +1 -0
  74. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/popover/Popover.vue.d.ts +65 -0
  75. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/popover/index.d.ts +1 -0
  76. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/separator/Separator.vue.d.ts +35 -0
  77. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/separator/index.d.ts +1 -0
  78. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/spacer/Spacer.vue.d.ts +31 -0
  79. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/spacer/index.d.ts +1 -0
  80. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/toolbar/Toolbar.vue.d.ts +45 -0
  81. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/toolbar/ToolbarGroup.vue.d.ts +15 -0
  82. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/toolbar/ToolbarSeparator.vue.d.ts +3 -0
  83. package/dist/types/richtext-vue/src/components/tiptap-ui-primitive/toolbar/index.d.ts +3 -0
  84. package/dist/types/richtext-vue/src/composables/use-editor-state.d.ts +32 -0
  85. package/dist/types/richtext-vue/src/composables/use-is-breakpoint.d.ts +10 -0
  86. package/dist/types/richtext-vue/src/composables/use-menu-navigation.d.ts +55 -0
  87. package/dist/types/richtext-vue/src/composables/use-popover.d.ts +45 -0
  88. package/dist/types/richtext-vue/src/composables/use-tiptap-editor.d.ts +16 -0
  89. package/dist/types/richtext-vue/src/composables/use-top-layer.d.ts +9 -0
  90. package/dist/types/richtext-vue/src/composables/use-translations.d.ts +7 -0
  91. package/dist/types/richtext-vue/src/composables/utils.d.ts +21 -0
  92. package/dist/types/richtext-vue/src/index.d.ts +14 -0
  93. package/dist/types/richtext-vue/src/lib/pointer.d.ts +6 -0
  94. package/dist/types/richtext-vue/src/lib/resize-image.d.ts +6 -0
  95. package/dist/types/richtext-vue/src/lib/shortcuts.d.ts +24 -0
  96. package/dist/types/richtext-vue/src/lib/throttle.d.ts +10 -0
  97. package/dist/types/richtext-vue/src/lib/tiptap-utils.d.ts +109 -0
  98. package/dist/types/richtext-vue/src/locales/en.d.ts +140 -0
  99. package/dist/types/richtext-vue/src/locales/index.d.ts +5 -0
  100. package/dist/types/richtext-vue/src/locales/nl.d.ts +2 -0
  101. package/dist/types/richtext-vue2/src/components/tiptap-ui-primitive/portal/Portal.vue.d.ts +15 -0
  102. package/dist/types/richtext-vue2/src/lib/tiptap-vue.d.ts +9 -0
  103. package/dist/v-richtext.js +3318 -0
  104. package/dist/v-richtext.js.map +1 -0
  105. package/package.json +64 -6
  106. package/README.md +0 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v-richtext.js","sources":["../src/lib/tiptap-vue.ts","../../richtext-vue/src/lib/throttle.ts","../../richtext-vue/src/lib/resize-image.ts","../../richtext-vue/src/lib/pointer.ts","../../richtext-vue/src/lib/tiptap-utils.ts","../../richtext-vue/src/components/tiptap-ui-primitive/button/Button.vue","../../richtext-vue/src/components/tiptap-ui-primitive/spacer/Spacer.vue","../../richtext-vue/src/locales/en.ts","../../richtext-vue/src/locales/nl.ts","../../richtext-vue/src/locales/index.ts","../../richtext-vue/src/composables/use-translations.ts","../../richtext-vue/src/composables/utils.ts","../../richtext-vue/src/composables/use-menu-navigation.ts","../../richtext-vue/src/components/tiptap-ui-primitive/toolbar/Toolbar.vue","../../richtext-vue/src/composables/use-tiptap-editor.ts","../../richtext-vue/src/composables/use-editor-state.ts","../../richtext-vue/src/components/tiptap-ui/command.ts","../../richtext-vue/src/components/tiptap-icons/index.ts","../../richtext-vue/src/components/tiptap-ui/heading-button/use-heading.ts","../../richtext-vue/src/components/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.ts","../../richtext-vue/src/components/tiptap-icons/sprite.svg?raw","../../richtext-vue/src/components/tiptap-icons/Icon.vue","../../richtext-vue/src/components/tiptap-ui/CommandButton.vue","../../richtext-vue/src/composables/use-popover.ts","../src/components/tiptap-ui-primitive/portal/Portal.vue","../../richtext-vue/src/components/tiptap-ui-primitive/dropdown-menu/DropdownMenu.vue","../../richtext-vue/src/components/tiptap-ui/heading-dropdown-menu/HeadingDropdownMenu.vue","../../richtext-vue/src/components/tiptap-ui/list-button/use-list.ts","../../richtext-vue/src/components/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts","../../richtext-vue/src/components/tiptap-ui/list-dropdown-menu/ListDropdownMenu.vue","../../richtext-vue/src/components/tiptap-ui/blockquote-button/use-blockquote.ts","../../richtext-vue/src/components/tiptap-ui/code-block-button/use-code-block.ts","../../richtext-vue/src/components/tiptap-ui/color-highlight-button/use-color-highlight.ts","../../richtext-vue/src/components/tiptap-ui-primitive/popover/Popover.vue","../../richtext-vue/src/components/tiptap-ui/color-highlight-popover/ColorHighlightPopoverButton.vue","../../richtext-vue/src/composables/use-is-breakpoint.ts","../../richtext-vue/src/components/tiptap-ui/color-highlight-popover/ColorHighlightPopoverContent.vue","../../richtext-vue/src/components/tiptap-ui/color-highlight-popover/ColorHighlightPopover.vue","../../richtext-vue/src/lib/shortcuts.ts","../../richtext-vue/src/components/tiptap-ui/link-popover/use-link-popover.ts","../../richtext-vue/src/components/tiptap-ui/link-popover/LinkButton.vue","../../richtext-vue/src/components/tiptap-ui-primitive/input/Input.vue","../../richtext-vue/src/components/tiptap-ui/link-popover/LinkMain.vue","../../richtext-vue/src/components/tiptap-ui/link-popover/LinkPopover.vue","../../richtext-vue/src/components/tiptap-ui/link-popover/LinkContent.vue","../../richtext-vue/src/components/tiptap-ui/mark-button/use-mark.ts","../../richtext-vue/src/components/tiptap-ui/text-align-button/use-text-align.ts","../../richtext-vue/src/components/tiptap-ui/undo-redo-button/use-undo-redo.ts","../../richtext-vue/src/composables/use-top-layer.ts","../../richtext-vue/src/components/input-richtext/MentionMenu.vue","../../richtext-vue/src/components/input-richtext/extensions/previews.ts","../../richtext-vue/src/components/input-richtext/extensions/visibility-conditions.ts","../../richtext-vue/src/components/input-richtext/VisibilityConditionsMenu.vue","../../richtext-vue/src/components/input-richtext/extensions/file-node.ts","../../richtext-vue/src/components/input-richtext/UploadPopover.vue","../../richtext-vue/src/components/input-richtext/YoutubePopover.vue","../../richtext-vue/src/components/input-richtext/TableMenu.vue","../../richtext-vue/src/components/input-richtext/liquid.ts","../../richtext-vue/src/components/input-richtext/TemplatesMenu.vue","../../richtext-vue/src/components/input-richtext/MediaControls.vue","../../richtext-vue/src/components/input-richtext/CharacterCount.vue","../../richtext-vue/src/components/input-richtext/TableOfContents.vue","../../richtext-vue/src/components/input-richtext/extensions/mention.ts","../../richtext-vue/src/components/input-richtext/extensions/media-attributes.ts","../../richtext-vue/src/components/input-richtext/extensions/file-drop.ts","../../richtext-vue/src/components/input-richtext/extensions/link.ts","../../richtext-vue/src/components/input-richtext/extensions/task-item.ts","../../richtext-vue/src/components/input-richtext/extensions/heading-id.ts","../../richtext-vue/src/components/input-richtext/extensions/index.ts","../../richtext-vue/src/components/input-richtext/extensions/mention-menu.ts","../../richtext-vue/src/components/input-richtext/InputRichtext.vue","../../richtext-vue/src/index.ts"],"sourcesContent":["import { onBeforeUnmount, onMounted, shallowRef } from \"vue\"\nimport { Editor, EditorContent } from \"@tiptap/vue-2\"\nimport type { EditorOptions } from \"@tiptap/core\"\n\n/**\n * Vue 2 version of ../../../richtext-vue/src/lib/tiptap-vue.ts. @tiptap/vue-2\n * has no `useEditor`, so this is the same as @tiptap/vue-3's: the editor is\n * created on mount and destroyed on unmount.\n */\nexport function useEditor(options: Partial<EditorOptions> = {}) {\n const editor = shallowRef<Editor>()\n\n onMounted(() => {\n editor.value = new Editor(options)\n })\n onBeforeUnmount(() => {\n editor.value?.destroy()\n })\n\n return editor\n}\n\nexport { EditorContent }\n","/**\n * Calls `fn` at most once per `wait` ms: right away, then once more at the end\n * of the wait if it was called again in the meantime (lodash's defaults).\n * p5: replaces lodash.throttle, which was the only use of that dependency.\n */\nexport function throttle(fn: () => void, wait: number) {\n let timer: ReturnType<typeof setTimeout> | null = null\n let pending = false\n\n const run = () => {\n pending = false\n fn()\n }\n\n const throttled = () => {\n if (timer) {\n pending = true\n return\n }\n run()\n timer = setTimeout(function next() {\n if (pending) {\n run()\n timer = setTimeout(next, wait)\n } else {\n timer = null\n }\n }, wait)\n }\n\n /** Runs a pending call now */\n throttled.flush = () => {\n if (pending) run()\n }\n\n /** Drops a pending call */\n throttled.cancel = () => {\n pending = false\n if (timer) clearTimeout(timer)\n timer = null\n }\n\n return throttled\n}\n","/** Formats the browser can re-encode; GIF (animation) and SVG are left as they are */\nconst RESIZABLE_TYPES = [\"image/jpeg\", \"image/png\", \"image/webp\"]\n\n/**\n * p5: scales an image down to `maxWidth` (keeping its ratio) before it's\n * uploaded. Returns the file itself when it's already narrow enough, isn't a\n * resizable format, or can't be decoded.\n */\nexport async function resizeImage(file: File, maxWidth: number): Promise<File> {\n if (!maxWidth || !RESIZABLE_TYPES.includes(file.type)) return file\n\n let bitmap: ImageBitmap\n try {\n // Applies the EXIF orientation, so phone photos stay upright\n bitmap = await createImageBitmap(file)\n } catch {\n return file\n }\n\n try {\n if (bitmap.width <= maxWidth) return file\n\n const width = maxWidth\n const height = Math.round((bitmap.height * maxWidth) / bitmap.width)\n const canvas = document.createElement(\"canvas\")\n canvas.width = width\n canvas.height = height\n const context = canvas.getContext(\"2d\")\n if (!context) return file\n\n context.imageSmoothingQuality = \"high\"\n context.drawImage(bitmap, 0, 0, width, height)\n\n const blob = await new Promise<Blob | null>((resolve) =>\n canvas.toBlob(resolve, file.type, 0.9)\n )\n if (!blob) return file\n\n return new File([blob], file.name, { type: file.type, lastModified: file.lastModified })\n } finally {\n bitmap.close()\n }\n}\n","// Whether a mouse button / touch is currently pressed anywhere on the page\nlet pointerDown = false\nif (typeof document !== \"undefined\") {\n const release = () => (pointerDown = false)\n document.addEventListener(\"pointerdown\", () => (pointerDown = true), true)\n document.addEventListener(\"pointerup\", release, true)\n document.addEventListener(\"pointercancel\", release, true)\n}\n\nexport const isPointerDown = () => pointerDown\n\n/**\n * Runs `callback` once the current pointer press has fully ended, including\n * the click it causes: the timeout lets the browser dispatch that first.\n */\nexport function afterPointerRelease(callback: () => void) {\n const done = () => {\n document.removeEventListener(\"pointerup\", done, true)\n document.removeEventListener(\"pointercancel\", done, true)\n setTimeout(callback)\n }\n document.addEventListener(\"pointerup\", done, true)\n document.addEventListener(\"pointercancel\", done, true)\n}\n","// p5: trimmed to the helpers the plugin uses (see UPSTREAM.md). Without the\n// table-selection helper, @tiptap/pm/tables is no longer always imported.\nimport type { Node as PMNode } from \"@tiptap/pm/model\"\nimport { AllSelection, NodeSelection, TextSelection } from \"@tiptap/pm/state\"\nimport type { Editor } from \"@tiptap/core\"\n\nexport const MAX_FILE_SIZE = 5 * 1024 * 1024 // 5MB\n\nexport const MAC_SYMBOLS: Record<string, string> = {\n mod: \"⌘\",\n command: \"⌘\",\n meta: \"⌘\",\n ctrl: \"⌃\",\n control: \"⌃\",\n alt: \"⌥\",\n option: \"⌥\",\n shift: \"⇧\",\n backspace: \"Del\",\n delete: \"⌦\",\n enter: \"⏎\",\n escape: \"⎋\",\n capslock: \"⇪\",\n} as const\n\n/**\n * Determines if the current platform is macOS\n * @returns boolean indicating if the current platform is Mac\n */\nexport function isMac(): boolean {\n return (\n typeof navigator !== \"undefined\" &&\n navigator.platform.toLowerCase().includes(\"mac\")\n )\n}\n\n/**\n * Formats a shortcut key based on the platform (Mac or non-Mac)\n * @param key - The key to format (e.g., \"ctrl\", \"alt\", \"shift\")\n * @param isMac - Boolean indicating if the platform is Mac\n * @param capitalize - Whether to capitalize the key (default: true)\n * @returns Formatted shortcut key symbol\n */\nexport const formatShortcutKey = (\n key: string,\n isMac: boolean,\n capitalize: boolean = true\n) => {\n if (isMac) {\n const lowerKey = key.toLowerCase()\n return MAC_SYMBOLS[lowerKey] || (capitalize ? key.toUpperCase() : key)\n }\n\n // p5: the template shows \"Mod\" on Windows/Linux, where it means Ctrl\n if (key.toLowerCase() === \"mod\") return \"Ctrl\"\n\n return capitalize ? key.charAt(0).toUpperCase() + key.slice(1) : key\n}\n\n/**\n * Parses a shortcut key string into an array of formatted key symbols\n * @param shortcutKeys - The string of shortcut keys (e.g., \"ctrl-alt-shift\")\n * @param delimiter - The delimiter used to split the keys (default: \"-\")\n * @param capitalize - Whether to capitalize the keys (default: true)\n * @returns Array of formatted shortcut key symbols\n */\nexport const parseShortcutKeys = (props: {\n shortcutKeys: string | undefined\n delimiter?: string\n capitalize?: boolean\n}) => {\n const { shortcutKeys, delimiter = \"+\", capitalize = true } = props\n\n if (!shortcutKeys) return []\n\n return shortcutKeys\n .split(delimiter)\n .map((key) => key.trim())\n .map((key) => formatShortcutKey(key, isMac(), capitalize))\n}\n\n/**\n * Checks if a mark exists in the editor schema\n * @param markName - The name of the mark to check\n * @param editor - The editor instance\n * @returns boolean indicating if the mark exists in the schema\n */\nexport const isMarkInSchema = (\n markName: string,\n editor: Editor | null\n): boolean => {\n if (!editor?.schema) return false\n return editor.schema.spec.marks.get(markName) !== undefined\n}\n\n/**\n * Checks if a node exists in the editor schema\n * @param nodeName - The name of the node to check\n * @param editor - The editor instance\n * @returns boolean indicating if the node exists in the schema\n */\nexport const isNodeInSchema = (\n nodeName: string,\n editor: Editor | null\n): boolean => {\n if (!editor?.schema) return false\n return editor.schema.spec.nodes.get(nodeName) !== undefined\n}\n\n/**\n * Checks if a value is a valid number (not null, undefined, or NaN)\n * @param value - The value to check\n * @returns boolean indicating if the value is a valid number\n */\nexport function isValidPosition(pos: number | null | undefined): pos is number {\n return typeof pos === \"number\" && pos >= 0\n}\n\n/**\n * Checks if one or more extensions are registered in the Tiptap editor.\n * @param editor - The Tiptap editor instance\n * @param extensionNames - A single extension name or an array of names to check\n * @returns True if at least one of the extensions is available, false otherwise\n */\n// This check runs from render/selection paths, so an absent extension would\n// otherwise repeat the hint on every update — remember what was already\n// reported and say it once.\nconst warnedMissingExtensions = new Set<string>()\n\nexport function isExtensionAvailable(\n editor: Editor | null,\n extensionNames: string | string[]\n): boolean {\n if (!editor || editor.isDestroyed) return false\n\n const names = Array.isArray(extensionNames)\n ? extensionNames\n : [extensionNames]\n\n const extensions = editor.extensionManager?.extensions\n if (!extensions) return false\n\n const found = names.some((name) =>\n extensions.some((ext) => ext.name === name)\n )\n\n if (!found) {\n const key = names.join(\", \")\n\n if (!warnedMissingExtensions.has(key)) {\n warnedMissingExtensions.add(key)\n console.warn(\n `None of the extensions [${key}] were found in the editor schema. Ensure they are included in the editor configuration.`\n )\n }\n }\n\n return found\n}\n\n/**\n * Finds a node at the specified position with error handling\n * @param editor The Tiptap editor instance\n * @param position The position in the document to find the node\n * @returns The node at the specified position, or null if not found\n */\nexport function findNodeAtPosition(editor: Editor, position: number) {\n try {\n const node = editor.state.doc.nodeAt(position)\n if (!node) {\n console.warn(`No node found at position ${position}`)\n return null\n }\n return node\n } catch (error) {\n console.error(`Error getting node at position ${position}:`, error)\n return null\n }\n}\n\n/**\n * Finds the position and instance of a node in the document\n * @param props Object containing editor, node (optional), and nodePos (optional)\n * @param props.editor The Tiptap editor instance\n * @param props.node The node to find (optional if nodePos is provided)\n * @param props.nodePos The position of the node to find (optional if node is provided)\n * @returns An object with the position and node, or null if not found\n */\nexport function findNodePosition(props: {\n editor: Editor | null\n node?: PMNode | null\n nodePos?: number | null\n}): { pos: number; node: PMNode } | null {\n const { editor, node, nodePos } = props\n\n if (!editor || !editor.state?.doc) return null\n\n // Zero is valid position\n const hasValidNode = node !== undefined && node !== null\n const hasValidPos = isValidPosition(nodePos)\n\n if (!hasValidNode && !hasValidPos) {\n return null\n }\n\n // First search for the node in the document if we have a node\n if (hasValidNode) {\n let foundPos = -1\n let foundNode: PMNode | null = null\n\n editor.state.doc.descendants((currentNode, pos) => {\n // TODO: Needed?\n // if (currentNode.type && currentNode.type.name === node!.type.name) {\n if (currentNode === node) {\n foundPos = pos\n foundNode = currentNode\n return false\n }\n return true\n })\n\n if (foundPos !== -1 && foundNode !== null) {\n return { pos: foundPos, node: foundNode }\n }\n }\n\n // If we have a valid position, use findNodeAtPosition\n if (hasValidPos) {\n const nodeAtPos = findNodeAtPosition(editor, nodePos!)\n if (nodeAtPos) {\n return { pos: nodePos!, node: nodeAtPos }\n }\n }\n\n return null\n}\n\n/**\n * Determines whether the current selection contains a node whose type matches\n * any of the provided node type names.\n * @param editor Tiptap editor instance\n * @param nodeTypeNames List of node type names to match against\n * @param checkAncestorNodes Whether to check ancestor node types up the depth chain\n */\nexport function isNodeTypeSelected(\n editor: Editor | null,\n nodeTypeNames: string[] = [],\n checkAncestorNodes: boolean = false\n): boolean {\n if (!editor || !editor.state.selection) return false\n\n const { selection } = editor.state\n if (selection.empty) return false\n\n // Direct node selection check\n if (selection instanceof NodeSelection) {\n const selectedNode = selection.node\n return selectedNode ? nodeTypeNames.includes(selectedNode.type.name) : false\n }\n\n // Depth-based ancestor node check\n if (checkAncestorNodes) {\n const { $from } = selection\n for (let depth = $from.depth; depth > 0; depth--) {\n const ancestorNode = $from.node(depth)\n if (nodeTypeNames.includes(ancestorNode.type.name)) {\n return true\n }\n }\n }\n\n return false\n}\n\n/**\n * Check whether the current selection is fully within nodes\n * whose type names are in the provided `types` list.\n *\n * - NodeSelection → checks the selected node.\n * - Text/AllSelection → ensures all textblocks within [from, to) are allowed.\n */\nexport function selectionWithinConvertibleTypes(\n editor: Editor,\n types: string[] = []\n): boolean {\n if (!editor || types.length === 0) return false\n\n const { state } = editor\n const { selection } = state\n const allowed = new Set(types)\n\n if (selection instanceof NodeSelection) {\n const nodeType = selection.node?.type?.name\n return !!nodeType && allowed.has(nodeType)\n }\n\n if (selection instanceof TextSelection || selection instanceof AllSelection) {\n let valid = true\n state.doc.nodesBetween(selection.from, selection.to, (node) => {\n if (node.isTextblock && !allowed.has(node.type.name)) {\n valid = false\n return false // stop early\n }\n return valid\n })\n return valid\n }\n\n return false\n}\n\ntype ProtocolOptions = {\n /**\n * The protocol scheme to be registered.\n * @default '''\n * @example 'ftp'\n * @example 'git'\n */\n scheme: string\n\n /**\n * If enabled, it allows optional slashes after the protocol.\n * @default false\n * @example true\n */\n optionalSlashes?: boolean\n}\n\ntype ProtocolConfig = Array<ProtocolOptions | string>\n\nconst ATTR_WHITESPACE =\n // eslint-disable-next-line no-control-regex\n /[\\u0000-\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u2029\\u205F\\u3000]/g\n\nexport function isAllowedUri(\n uri: string | undefined,\n protocols?: ProtocolConfig\n) {\n const allowedProtocols: string[] = [\n \"http\",\n \"https\",\n \"ftp\",\n \"ftps\",\n \"mailto\",\n \"tel\",\n \"callto\",\n \"sms\",\n \"cid\",\n \"xmpp\",\n ]\n\n if (protocols) {\n protocols.forEach((protocol) => {\n const nextProtocol =\n typeof protocol === \"string\" ? protocol : protocol.scheme\n\n if (nextProtocol) {\n allowedProtocols.push(nextProtocol)\n }\n })\n }\n\n return (\n !uri ||\n uri.replace(ATTR_WHITESPACE, \"\").match(\n new RegExp(\n // eslint-disable-next-line no-useless-escape\n `^(?:(?:${allowedProtocols.join(\"|\")}):|[^a-z]|[a-z0-9+.\\-]+(?:[^a-z+.\\-:]|$))`,\n \"i\"\n )\n )\n )\n}\n\nexport function sanitizeUrl(\n inputUrl: string,\n baseUrl: string,\n protocols?: ProtocolConfig\n): string {\n try {\n const url = new URL(inputUrl, baseUrl)\n\n if (isAllowedUri(url.href, protocols)) {\n return url.href\n }\n } catch {\n // If URL creation fails, it's considered invalid\n }\n return \"#\"\n}\n\nexport function getSelectedBlockNodes(editor: Editor): PMNode[] {\n const { doc } = editor.state\n const { from, to } = editor.state.selection\n\n const blocks: PMNode[] = []\n const seen = new Set<number>()\n\n doc.nodesBetween(from, to, (node, pos) => {\n if (!node.isBlock) return\n\n if (!seen.has(pos)) {\n seen.add(pos)\n blocks.push(node)\n }\n\n return false\n })\n\n return blocks\n}\n","<template>\r\n <button\r\n data-slot=\"tiptap-button\"\r\n class=\"tiptap-button\"\r\n :title=\"title\"\r\n @click=\"emit('click', $event)\"\r\n @mousedown=\"emit('mousedown', $event)\"\r\n >\r\n <slot />\r\n </button>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from \"vue\"\r\nimport { isMac, parseShortcutKeys } from \"@/lib/tiptap-utils\"\r\nimport \"@/components/tiptap-ui-primitive/button/button.scss\"\r\n\r\n/**\r\n * p5: always the template's \"ghost\" style; its variants, sizes and check\r\n * variant are left out (see UPSTREAM.md).\r\n */\r\nconst props = withDefaults(\r\n defineProps<{\r\n /** Shown as the native title tooltip, together with shortcutKeys */\r\n tooltip?: string\r\n showTooltip?: boolean\r\n shortcutKeys?: string\r\n }>(),\r\n { showTooltip: true }\r\n)\r\n\r\n// Emitted (not passed through) so listeners also work in Vue 2\r\nconst emit = defineEmits<{\r\n (e: \"click\", event: MouseEvent): void\r\n (e: \"mousedown\", event: MouseEvent): void\r\n}>()\r\n\r\n// Native title instead of the template's floating tooltip\r\nconst title = computed(() => {\r\n if (!props.tooltip || !props.showTooltip) return undefined\r\n const shortcut = parseShortcutKeys({ shortcutKeys: props.shortcutKeys }).join(\r\n isMac() ? \"\" : \"+\"\r\n )\r\n return shortcut ? `${props.tooltip} (${shortcut})` : props.tooltip\r\n})\r\n</script>\r\n","<template>\n <div :style=\"style\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\"\n\nconst props = withDefaults(\n defineProps<{\n orientation?: \"horizontal\" | \"vertical\"\n size?: string | number\n }>(),\n { orientation: \"horizontal\" }\n)\n\nconst style = computed(() => ({\n ...(props.orientation === \"horizontal\" && !props.size && { flex: 1 }),\n ...(props.size && {\n width: props.orientation === \"vertical\" ? \"1px\" : props.size,\n height: props.orientation === \"horizontal\" ? \"1px\" : props.size,\n }),\n}))\n</script>\n","/**\r\n * English texts. Also defines the shape of every locale: a text missing in\r\n * another locale is a type error.\r\n */\r\nexport const en = {\r\n placeholder: \"Start typing...\",\r\n toolbar: \"Toolbar\",\r\n tableOfContents: \"Contents\",\r\n back: \"Back\",\r\n undo: \"Undo\",\r\n redo: \"Redo\",\r\n marks: {\r\n bold: \"Bold\",\r\n italic: \"Italic\",\r\n strike: \"Strikethrough\",\r\n underline: \"Underline\",\r\n code: \"Code\",\r\n superscript: \"Superscript\",\r\n subscript: \"Subscript\",\r\n },\r\n heading: {\r\n button: \"Heading\",\r\n menu: \"Format text as heading\",\r\n level: (level: number) => `Heading ${level}`,\r\n },\r\n list: {\r\n button: \"List\",\r\n menu: \"List options\",\r\n bulletList: \"Bullet list\",\r\n orderedList: \"Numbered list\",\r\n taskList: \"Task list\",\r\n /** Screen reader label of a task's checkbox */\r\n taskCheckbox: (text: string) => `Task: ${text || \"empty task\"}`,\r\n },\r\n blockquote: \"Blockquote\",\r\n codeBlock: \"Code block\",\r\n align: {\r\n left: \"Align left\",\r\n center: \"Align center\",\r\n right: \"Align right\",\r\n justify: \"Justify\",\r\n },\r\n highlight: {\r\n button: \"Highlight\",\r\n menu: \"Highlight colors\",\r\n remove: \"Remove highlight\",\r\n colors: {\r\n default: \"Default\",\r\n gray: \"Gray\",\r\n brown: \"Brown\",\r\n orange: \"Orange\",\r\n yellow: \"Yellow\",\r\n green: \"Green\",\r\n blue: \"Blue\",\r\n purple: \"Purple\",\r\n pink: \"Pink\",\r\n red: \"Red\",\r\n },\r\n },\r\n link: {\r\n button: \"Link\",\r\n placeholder: \"Paste a link...\",\r\n apply: \"Apply link\",\r\n newTab: \"Open in new tab\",\r\n open: \"Open link\",\r\n remove: \"Remove link\",\r\n },\r\n image: {\r\n button: \"Image\",\r\n choose: \"Choose image\",\r\n open: \"Open image\",\r\n remove: \"Remove image\",\r\n add: \"Add image\",\r\n },\r\n file: {\r\n button: \"File\",\r\n choose: \"Choose file\",\r\n open: \"Open file\",\r\n remove: \"Remove file\",\r\n add: \"Add file\",\r\n },\r\n upload: {\r\n pasteLink: \"Paste a link...\",\r\n orPasteLink: \"Or paste a link...\",\r\n uploading: \"Uploading…\",\r\n uploadingFile: (name: string) => `Uploading ${name}…`,\r\n dismiss: \"Dismiss\",\r\n failed: \"Upload failed\",\r\n tooLarge: (megabytes: number) => `Larger than ${megabytes} MB`,\r\n },\r\n youtube: {\r\n button: \"YouTube video\",\r\n placeholder: \"Paste a YouTube link...\",\r\n add: \"Add video\",\r\n remove: \"Remove video\",\r\n invalid: \"That doesn't look like a YouTube link\",\r\n },\r\n table: {\r\n button: \"Table\",\r\n insert: \"Insert table\",\r\n addRowBefore: \"Add row above\",\r\n addRowAfter: \"Add row below\",\r\n deleteRow: \"Delete row\",\r\n addColumnBefore: \"Add column before\",\r\n addColumnAfter: \"Add column after\",\r\n deleteColumn: \"Delete column\",\r\n toggleHeaderRow: \"Toggle header row\",\r\n mergeOrSplit: \"Merge or split cells\",\r\n delete: \"Delete table\",\r\n },\r\n templates: {\r\n button: \"Insert template\",\r\n },\r\n visibilityConditions: {\r\n button: \"Visibility condition\",\r\n remove: \"Remove condition\",\r\n hidden: (label: string) => `Block not visible: ${label}`,\r\n },\r\n media: {\r\n toolbar: \"Media options\",\r\n caption: \"Caption\",\r\n small: \"Small\",\r\n medium: \"Medium\",\r\n large: \"Large (full width)\",\r\n floatLeft: \"Float left\",\r\n floatNone: \"Centered, no float\",\r\n floatRight: \"Float right\",\r\n },\r\n mentions: {\r\n noResults: \"No results\",\r\n },\r\n characterCount: {\r\n count: (count: number) => (count === 1 ? \"1 character\" : `${count} characters`),\r\n atLeast: (min: number) => `At least ${min}`,\r\n },\r\n validation: {\r\n minCharacters: (min: number, current: number) =>\r\n `Use at least ${min} characters (currently ${current}).`,\r\n },\r\n}\r\n\r\nexport type RichtextMessages = typeof en\r\n","import type { RichtextMessages } from \"./en\"\r\n\r\nexport const nl: RichtextMessages = {\r\n placeholder: \"Begin met typen...\",\r\n toolbar: \"Werkbalk\",\r\n tableOfContents: \"Inhoud\",\r\n back: \"Terug\",\r\n undo: \"Ongedaan maken\",\r\n redo: \"Opnieuw\",\r\n marks: {\r\n bold: \"Vet\",\r\n italic: \"Cursief\",\r\n strike: \"Doorhalen\",\r\n underline: \"Onderstrepen\",\r\n code: \"Code\",\r\n superscript: \"Superscript\",\r\n subscript: \"Subscript\",\r\n },\r\n heading: {\r\n button: \"Kop\",\r\n menu: \"Tekst opmaken als kop\",\r\n level: (level) => `Kop ${level}`,\r\n },\r\n list: {\r\n button: \"Lijst\",\r\n menu: \"Lijstopties\",\r\n bulletList: \"Opsomming\",\r\n orderedList: \"Genummerde lijst\",\r\n taskList: \"Takenlijst\",\r\n taskCheckbox: (text) => `Taak: ${text || \"lege taak\"}`,\r\n },\r\n blockquote: \"Citaat\",\r\n codeBlock: \"Codeblok\",\r\n align: {\r\n left: \"Links uitlijnen\",\r\n center: \"Centreren\",\r\n right: \"Rechts uitlijnen\",\r\n justify: \"Uitvullen\",\r\n },\r\n highlight: {\r\n button: \"Markeren\",\r\n menu: \"Markeerkleuren\",\r\n remove: \"Markering verwijderen\",\r\n colors: {\r\n default: \"Standaard\",\r\n gray: \"Grijs\",\r\n brown: \"Bruin\",\r\n orange: \"Oranje\",\r\n yellow: \"Geel\",\r\n green: \"Groen\",\r\n blue: \"Blauw\",\r\n purple: \"Paars\",\r\n pink: \"Roze\",\r\n red: \"Rood\",\r\n },\r\n },\r\n link: {\r\n button: \"Link\",\r\n placeholder: \"Plak een link...\",\r\n apply: \"Link toepassen\",\r\n newTab: \"Openen in nieuw tabblad\",\r\n open: \"Link openen\",\r\n remove: \"Link verwijderen\",\r\n },\r\n image: {\r\n button: \"Afbeelding\",\r\n choose: \"Kies afbeelding\",\r\n open: \"Afbeelding openen\",\r\n remove: \"Afbeelding verwijderen\",\r\n add: \"Afbeelding toevoegen\",\r\n },\r\n file: {\r\n button: \"Bestand\",\r\n choose: \"Kies bestand\",\r\n open: \"Bestand openen\",\r\n remove: \"Bestand verwijderen\",\r\n add: \"Bestand toevoegen\",\r\n },\r\n upload: {\r\n pasteLink: \"Plak een link...\",\r\n orPasteLink: \"Of plak een link...\",\r\n uploading: \"Uploaden…\",\r\n uploadingFile: (name) => `${name} uploaden…`,\r\n dismiss: \"Sluiten\",\r\n failed: \"Uploaden mislukt\",\r\n tooLarge: (megabytes) => `Groter dan ${megabytes} MB`,\r\n },\r\n youtube: {\r\n button: \"YouTube-video\",\r\n placeholder: \"Plak een YouTube-link...\",\r\n add: \"Video toevoegen\",\r\n remove: \"Video verwijderen\",\r\n invalid: \"Dit lijkt geen YouTube-link\",\r\n },\r\n table: {\r\n button: \"Tabel\",\r\n insert: \"Tabel invoegen\",\r\n addRowBefore: \"Rij erboven invoegen\",\r\n addRowAfter: \"Rij eronder invoegen\",\r\n deleteRow: \"Rij verwijderen\",\r\n addColumnBefore: \"Kolom links invoegen\",\r\n addColumnAfter: \"Kolom rechts invoegen\",\r\n deleteColumn: \"Kolom verwijderen\",\r\n toggleHeaderRow: \"Koprij aan/uit\",\r\n mergeOrSplit: \"Cellen samenvoegen of splitsen\",\r\n delete: \"Tabel verwijderen\",\r\n },\r\n templates: {\r\n button: \"Sjabloon invoegen\",\r\n },\r\n visibilityConditions: {\r\n button: \"Zichtbaarheidsvoorwaarde\",\r\n remove: \"Voorwaarde verwijderen\",\r\n hidden: (label) => `Blok niet zichtbaar: ${label}`,\r\n },\r\n media: {\r\n toolbar: \"Media-opties\",\r\n caption: \"Bijschrift\",\r\n small: \"Klein\",\r\n medium: \"Middel\",\r\n large: \"Groot (volle breedte)\",\r\n floatLeft: \"Links, tekst ernaast\",\r\n floatNone: \"Gecentreerd, zonder tekst ernaast\",\r\n floatRight: \"Rechts, tekst ernaast\",\r\n },\r\n mentions: {\r\n noResults: \"Geen resultaten\",\r\n },\r\n characterCount: {\r\n count: (count) => (count === 1 ? \"1 teken\" : `${count} tekens`),\r\n atLeast: (min) => `Minimaal ${min}`,\r\n },\r\n validation: {\r\n minCharacters: (min, current) => `Gebruik minimaal ${min} tekens (nu ${current}).`,\r\n },\r\n}\r\n","import { en, type RichtextMessages } from \"./en\"\nimport { nl } from \"./nl\"\nimport type { RichtextLocale } from \"@/components/input-richtext/types\"\n\nexport type { RichtextMessages }\n\nexport const DEFAULT_LOCALE: RichtextLocale = \"nl\"\n\nexport const locales: Record<RichtextLocale, RichtextMessages> = { nl, en }\n","import { computed, inject, provide, type ComputedRef, type InjectionKey } from \"vue\"\nimport { DEFAULT_LOCALE, locales, type RichtextMessages } from \"@/locales\"\nimport type { RichtextLocale } from \"@/components/input-richtext/types\"\n\nconst TranslationsKey: InjectionKey<ComputedRef<RichtextMessages>> =\n Symbol(\"richtext-translations\")\n\n/** p5: makes the texts for `locale` available to all components below */\nexport function provideTranslations(\n locale: () => RichtextLocale | undefined\n): ComputedRef<RichtextMessages> {\n const messages = computed(() => locales[locale() ?? DEFAULT_LOCALE] ?? locales[DEFAULT_LOCALE])\n provide(TranslationsKey, messages)\n return messages\n}\n\n/** The texts of the surrounding editor's locale, or the default locale */\nexport function useTranslations(): ComputedRef<RichtextMessages> {\n return inject(TranslationsKey, () => computed(() => locales[DEFAULT_LOCALE]), true)\n}\n","import { isRef, type Ref } from \"vue\"\n\n/**\n * A plain value, a ref, or a getter. Lets composables accept reactive props\n * (`() => props.type`) as well as static values.\n *\n * Deliberately not Vue's `MaybeRefOrGetter`/`toValue` (Vue 3.3+), so this also\n * works on Vue 2.7 should we ever port back.\n */\nexport type MaybeGetter<T> = T | Ref<T> | (() => T)\n\n/**\n * A config object whose values may be getters, except callbacks (`on*`),\n * which are passed through as-is.\n */\nexport type MaybeGetters<T> = {\n [K in keyof T]: K extends `on${string}` ? T[K] : MaybeGetter<T[K]>\n}\n\nexport function resolveValue<T>(value: MaybeGetter<T>): T {\n if (typeof value === \"function\") return (value as () => T)()\n if (isRef(value)) return value.value\n return value\n}\n\n/**\n * Compares primitives with Object.is and plain objects/arrays one level deep.\n */\nexport function shallowEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true\n if (\n typeof a !== \"object\" ||\n typeof b !== \"object\" ||\n a === null ||\n b === null\n ) {\n return false\n }\n\n const keysA = Object.keys(a)\n const keysB = Object.keys(b)\n if (keysA.length !== keysB.length) return false\n\n return keysA.every((key) =>\n Object.is(\n (a as Record<string, unknown>)[key],\n (b as Record<string, unknown>)[key]\n )\n )\n}\n","import type { Editor } from \"@tiptap/core\"\nimport { computed, ref, watch, type Ref } from \"vue\"\nimport { resolveValue, type MaybeGetter } from \"@/composables/utils\"\n\ntype Orientation = \"horizontal\" | \"vertical\" | \"both\"\n\ninterface MenuNavigationOptions<T> {\n /**\n * The Tiptap editor instance, if using with a Tiptap editor.\n */\n editor?: Ref<Editor | null | undefined>\n /**\n * The container element that receives the keyboard events.\n */\n containerRef?: Ref<HTMLElement | null | undefined>\n /**\n * Search query that affects the selected item.\n */\n query?: MaybeGetter<string | undefined>\n /**\n * Items to navigate through.\n */\n items: MaybeGetter<T[]>\n /**\n * Called when an item is selected.\n */\n onSelect?: (item: T) => void\n /**\n * Called when the menu should close.\n */\n onClose?: () => void\n /**\n * The navigation orientation of the menu.\n * @default \"vertical\"\n */\n orientation?: Orientation\n /**\n * Whether to automatically select the first item when the menu opens.\n * @default true\n */\n autoSelectFirstItem?: boolean\n /**\n * Whether Tab/Shift+Tab should loop through the items.\n * Disable this for ARIA toolbars, where Tab must leave the toolbar.\n * @default true\n */\n loopOnTab?: boolean\n}\n\n/**\n * Keyboard navigation for toolbars, dropdown menus and command palettes:\n * arrow keys, tab, home/end, enter to select and escape to close.\n */\nexport function useMenuNavigation<T>({\n editor,\n containerRef,\n query,\n items,\n onSelect,\n onClose,\n orientation = \"vertical\",\n autoSelectFirstItem = true,\n loopOnTab = true,\n}: MenuNavigationOptions<T>) {\n const index = ref(autoSelectFirstItem ? 0 : -1)\n\n const setSelectedIndex = (value: number) => (index.value = value)\n\n const handleKeyboardNavigation = (event: KeyboardEvent) => {\n const list = resolveValue(items)\n if (!list.length) return false\n\n const moveNext = () =>\n (index.value = index.value === -1 ? 0 : (index.value + 1) % list.length)\n const movePrev = () =>\n (index.value =\n index.value === -1\n ? list.length - 1\n : (index.value - 1 + list.length) % list.length)\n\n switch (event.key) {\n case \"ArrowUp\":\n if (orientation === \"horizontal\") return false\n event.preventDefault()\n movePrev()\n return true\n\n case \"ArrowDown\":\n if (orientation === \"horizontal\") return false\n event.preventDefault()\n moveNext()\n return true\n\n case \"ArrowLeft\":\n if (orientation === \"vertical\") return false\n event.preventDefault()\n movePrev()\n return true\n\n case \"ArrowRight\":\n if (orientation === \"vertical\") return false\n event.preventDefault()\n moveNext()\n return true\n\n case \"Tab\":\n if (!loopOnTab) return false\n event.preventDefault()\n if (event.shiftKey) movePrev()\n else moveNext()\n return true\n\n case \"Home\":\n event.preventDefault()\n index.value = 0\n return true\n\n case \"End\":\n event.preventDefault()\n index.value = list.length - 1\n return true\n\n case \"Enter\":\n if (event.isComposing) return false\n event.preventDefault()\n if (index.value !== -1 && list[index.value]) {\n onSelect?.(list[index.value])\n }\n return true\n\n case \"Escape\":\n if (!onClose) return false\n event.preventDefault()\n onClose()\n return true\n\n default:\n return false\n }\n }\n\n watch(\n () => editor?.value?.view.dom ?? containerRef?.value ?? null,\n (target, _previous, onCleanup) => {\n if (!target) return\n target.addEventListener(\"keydown\", handleKeyboardNavigation, true)\n onCleanup(() =>\n target.removeEventListener(\"keydown\", handleKeyboardNavigation, true)\n )\n },\n { immediate: true }\n )\n\n watch(\n () => resolveValue(query),\n (value) => {\n if (value) index.value = autoSelectFirstItem ? 0 : -1\n }\n )\n\n return {\n selectedIndex: computed(() =>\n resolveValue(items).length ? index.value : undefined\n ),\n setSelectedIndex,\n }\n}\n","<template>\r\n <div\r\n ref=\"toolbarRef\"\r\n role=\"toolbar\"\r\n :aria-label=\"t.toolbar\"\r\n :data-variant=\"variant\"\r\n class=\"tiptap-toolbar\"\r\n >\r\n <slot />\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { useTranslations } from \"@/composables/use-translations\"\r\nimport { onBeforeUnmount, onMounted, ref, shallowRef, watch } from \"vue\"\r\nimport { useMenuNavigation } from \"@/composables/use-menu-navigation\"\r\nimport \"@/components/tiptap-ui-primitive/toolbar/toolbar.scss\"\r\n\r\nconst t = useTranslations()\r\n\r\nwithDefaults(defineProps<{ variant?: \"floating\" | \"fixed\" }>(), {\r\n variant: \"fixed\",\r\n})\r\n\r\nconst toolbarRef = ref<HTMLDivElement | null>(null)\r\ndefineExpose({ el: toolbarRef })\r\n\r\n// --- Roving tabindex + arrow key navigation (the template's useToolbarNavigation) ---\r\n\r\nconst items = shallowRef<HTMLElement[]>([])\r\nlet initialContainerTabIndex: number | null | undefined = undefined\r\nlet observer: MutationObserver | null = null\r\n\r\nconst collectItems = () =>\r\n Array.from(\r\n toolbarRef.value?.querySelectorAll<HTMLElement>(\r\n 'button:not([disabled]), [role=\"button\"]:not([disabled]), [tabindex=\"0\"]:not([disabled])'\r\n ) ?? []\r\n )\r\n\r\nconst { selectedIndex, setSelectedIndex } = useMenuNavigation<HTMLElement>({\r\n containerRef: toolbarRef,\r\n items: () => items.value,\r\n orientation: \"horizontal\",\r\n onSelect: (el) => el.click(),\r\n autoSelectFirstItem: false,\r\n loopOnTab: false,\r\n})\r\n\r\nconst handleFocus = (e: FocusEvent) => {\r\n const toolbar = toolbarRef.value\r\n const target = e.target as HTMLElement\r\n if (!toolbar?.contains(target)) return\r\n\r\n target.setAttribute(\"data-focus-visible\", \"true\")\r\n const itemIndex = items.value.indexOf(target)\r\n if (itemIndex >= 0) setSelectedIndex(itemIndex)\r\n}\r\n\r\nconst handleBlur = (e: FocusEvent) => {\r\n const toolbar = toolbarRef.value\r\n if (!toolbar) return\r\n\r\n const target = e.target as HTMLElement\r\n if (toolbar.contains(target)) target.removeAttribute(\"data-focus-visible\")\r\n\r\n const nextTarget = e.relatedTarget\r\n const leavesToolbar = !(nextTarget instanceof Node) || !toolbar.contains(nextTarget)\r\n\r\n if (leavesToolbar && initialContainerTabIndex != null) {\r\n queueMicrotask(() => {\r\n if (toolbar.isConnected && !toolbar.contains(document.activeElement)) {\r\n toolbar.tabIndex = initialContainerTabIndex!\r\n setSelectedIndex(-1)\r\n }\r\n })\r\n }\r\n}\r\n\r\nonMounted(() => {\r\n const toolbar = toolbarRef.value!\r\n\r\n initialContainerTabIndex =\r\n toolbar.hasAttribute(\"tabindex\") && toolbar.tabIndex >= 0\r\n ? toolbar.tabIndex\r\n : null\r\n\r\n const updateItems = () => (items.value = collectItems())\r\n updateItems()\r\n observer = new MutationObserver(updateItems)\r\n observer.observe(toolbar, {\r\n childList: true,\r\n subtree: true,\r\n attributes: true,\r\n attributeFilter: [\"disabled\"],\r\n })\r\n\r\n toolbar.addEventListener(\"focus\", handleFocus, true)\r\n toolbar.addEventListener(\"blur\", handleBlur, true)\r\n})\r\n\r\nonBeforeUnmount(() => {\r\n observer?.disconnect()\r\n toolbarRef.value?.removeEventListener(\"focus\", handleFocus, true)\r\n toolbarRef.value?.removeEventListener(\"blur\", handleBlur, true)\r\n})\r\n\r\nwatch([selectedIndex, items], ([index, list]) => {\r\n const toolbar = toolbarRef.value\r\n if (!list.length || !toolbar) return\r\n\r\n const currentIndex = index ?? -1\r\n const focusIsInside = toolbar.contains(document.activeElement)\r\n\r\n if (initialContainerTabIndex != null) {\r\n list.forEach((item) => (item.tabIndex = -1))\r\n\r\n if (currentIndex >= 0 && list[currentIndex]) {\r\n toolbar.tabIndex = -1\r\n if (focusIsInside) list[currentIndex].focus()\r\n } else {\r\n toolbar.tabIndex = initialContainerTabIndex\r\n }\r\n return\r\n }\r\n\r\n const existingTabStop = list.findIndex((item) => item.tabIndex === 0)\r\n const activeIndex =\r\n currentIndex >= 0 && list[currentIndex]\r\n ? currentIndex\r\n : existingTabStop >= 0\r\n ? existingTabStop\r\n : 0\r\n\r\n list.forEach((item, i) => (item.tabIndex = i === activeIndex ? 0 : -1))\r\n\r\n // Only restore focus while it is inside the toolbar, so a popover that\r\n // autofocused its input isn't dismissed by focus being pulled back.\r\n if (currentIndex >= 0 && focusIsInside) list[activeIndex]?.focus()\r\n})\r\n</script>\r\n","import type { Editor } from \"@tiptap/core\"\nimport {\n computed,\n inject,\n provide,\n shallowRef,\n type ComputedRef,\n type InjectionKey,\n type Ref,\n} from \"vue\"\nimport { resolveValue, type MaybeGetter } from \"@/composables/utils\"\n\nconst TIPTAP_EDITOR_KEY: InjectionKey<Ref<Editor | null | undefined>> =\n Symbol(\"tiptap-editor\")\n\n/**\n * Makes the editor available to every tiptap-ui component below this one.\n * Replaces the React template's `EditorContext.Provider`.\n */\nexport function provideTiptapEditor(editor: Ref<Editor | null | undefined>) {\n provide(TIPTAP_EDITOR_KEY, editor)\n}\n\n/**\n * Returns the editor passed in explicitly (e.g. an `editor` prop), falling\n * back to the one provided by the nearest `provideTiptapEditor`.\n *\n * Unlike the React hook this does not re-render on every transaction; use\n * `useEditorState` to select the bits of state a component needs.\n */\nexport function useTiptapEditor(\n providedEditor?: MaybeGetter<Editor | null | undefined>\n): ComputedRef<Editor | null> {\n const injectedEditor = inject(TIPTAP_EDITOR_KEY, shallowRef(null))\n\n return computed(\n () => resolveValue(providedEditor) ?? injectedEditor.value ?? null\n )\n}\n","import type { Editor } from \"@tiptap/core\"\nimport {\n computed,\n shallowRef,\n watch,\n type ComputedRef,\n type Ref,\n type ShallowRef,\n} from \"vue\"\nimport { shallowEqual } from \"@/composables/utils\"\n\nconst EDITOR_EVENTS = [\"transaction\", \"focus\", \"blur\"] as const\n\nexport interface UseEditorStateOptions<T> {\n /**\n * Reactive inputs the selector depends on besides the editor (usually props),\n * so the state is recomputed when they change without a transaction.\n */\n deps?: () => unknown\n /**\n * Decides whether a newly selected value differs from the current one.\n * @default shallowEqual\n */\n equals?: (a: T, b: T) => boolean\n}\n\n/**\n * Selects a slice of editor state and only updates when that slice changes.\n *\n * The selector runs after every transaction (and on focus/blur), outside of\n * Vue's dependency tracking. The resulting ref is only written when the new\n * value differs, so a button re-renders only when its own state changes rather\n * than on every keystroke.\n *\n * Replaces React's `useEditorState` from @tiptap/react.\n */\nexport function useEditorState<T>(\n editor: Ref<Editor | null>,\n selector: (editor: Editor | null) => T,\n options: UseEditorStateOptions<T> = {}\n): Readonly<ShallowRef<T>> {\n const { deps, equals = shallowEqual } = options\n const state = shallowRef<T>(selector(editor.value))\n\n const update = () => {\n const next = selector(editor.value)\n if (!equals(state.value, next)) state.value = next\n }\n\n watch(\n editor,\n (currentEditor, _previous, onCleanup) => {\n update()\n if (!currentEditor) return\n\n EDITOR_EVENTS.forEach((event) => currentEditor.on(event, update))\n onCleanup(() =>\n EDITOR_EVENTS.forEach((event) => currentEditor.off(event, update))\n )\n },\n { immediate: true }\n )\n\n if (deps) watch(deps, update)\n\n return state\n}\n\n/**\n * Splits a selected state object into one computed ref per key, so it can be\n * destructured in `<script setup>` and used directly in templates.\n */\nexport function toStateRefs<T extends object>(\n state: Readonly<Ref<T>>\n): { [K in keyof T]: ComputedRef<T[K]> } {\n const refs = {} as { [K in keyof T]: ComputedRef<T[K]> }\n for (const key of Object.keys(state.value) as (keyof T)[]) {\n refs[key] = computed(() => state.value[key])\n }\n return refs\n}\n","import type { Editor } from \"@tiptap/core\"\r\nimport type { IconName } from \"@/components/tiptap-icons\"\r\nimport type { RichtextMessages } from \"@/locales\"\r\n\r\n/**\r\n * p5: a toolbar action, built from the template's framework-free helpers\r\n * (canToggleMark, toggleHeading, …) and rendered by CommandButton.vue.\r\n *\r\n * Replaces the template's per-button component + hook pairs (MarkButton +\r\n * useMark, HeadingButton + useHeading, …); see UPSTREAM.md.\r\n */\r\nexport interface EditorCommand {\r\n /** Tooltip and aria-label, in the editor's locale */\r\n label: (t: RichtextMessages) => string\r\n icon: IconName\r\n /** Shown in the tooltip. The shortcut itself is Tiptap's own keymap. */\r\n shortcutKeys?: string\r\n /** Omitted for actions without an on/off state, like undo */\r\n isActive?: (editor: Editor | null) => boolean\r\n canRun: (editor: Editor | null) => boolean\r\n run: (editor: Editor | null) => boolean\r\n}\r\n\r\nconst commands = new Map<string, EditorCommand>()\r\n\r\n/**\r\n * Commands never change, so each is created once. That keeps the `command`\r\n * prop stable, so buttons don't recompute their state on every render.\r\n */\r\nexport function defineCommand(key: string, create: () => EditorCommand): EditorCommand {\r\n let command = commands.get(key)\r\n if (!command) {\r\n command = create()\r\n commands.set(key, command)\r\n }\r\n return command\r\n}\r\n","// Icon ids for the symbols in ./sprite.svg: the template's icons that are used (see UPSTREAM.md)\r\nexport const AlignCenterIcon = \"align-center\" as const\r\nexport const AlignJustifyIcon = \"align-justify\" as const\r\nexport const AlignLeftIcon = \"align-left\" as const\r\nexport const AlignRightIcon = \"align-right\" as const\r\nexport const ArrowLeftIcon = \"arrow-left\" as const\r\nexport const BanIcon = \"ban\" as const\r\nexport const BlockquoteIcon = \"blockquote\" as const\r\nexport const BoldIcon = \"bold\" as const\r\nexport const ChevronDownIcon = \"chevron-down\" as const\r\nexport const CodeBlockIcon = \"code-block\" as const\r\nexport const Code2Icon = \"code2\" as const\r\nexport const CornerDownLeftIcon = \"corner-down-left\" as const\r\nexport const ExternalLinkIcon = \"external-link\" as const\r\nexport const HeadingFiveIcon = \"heading-five\" as const\r\nexport const HeadingFourIcon = \"heading-four\" as const\r\nexport const HeadingIcon = \"heading\" as const\r\nexport const HeadingOneIcon = \"heading-one\" as const\r\nexport const HeadingSixIcon = \"heading-six\" as const\r\nexport const HeadingThreeIcon = \"heading-three\" as const\r\nexport const HeadingTwoIcon = \"heading-two\" as const\r\nexport const HighlighterIcon = \"highlighter\" as const\r\nexport const ImagePlusIcon = \"image-plus\" as const\r\nexport const ItalicIcon = \"italic\" as const\r\nexport const LinkIcon = \"link\" as const\r\nexport const ListIcon = \"list\" as const\r\nexport const ListOrderedIcon = \"list-ordered\" as const\r\nexport const ListTodoIcon = \"list-todo\" as const\r\nexport const Redo2Icon = \"redo2\" as const\r\nexport const StrikeIcon = \"strike\" as const\r\nexport const SubscriptIcon = \"subscript\" as const\r\nexport const SuperscriptIcon = \"superscript\" as const\r\nexport const TrashIcon = \"trash\" as const\r\nexport const UnderlineIcon = \"underline\" as const\r\nexport const Undo2Icon = \"undo2\" as const\r\n\r\n// p5: added by hand (Lucide), not part of the template\r\nexport const EyeIcon = \"eye\" as const\r\nexport const PaperclipIcon = \"paperclip\" as const\r\nexport const YoutubeIcon = \"youtube\" as const\r\nexport const TableIcon = \"table\" as const\r\nexport const FileTextIcon = \"file-text\" as const\r\n\r\nexport type IconName =\r\n | \"align-center\"\r\n | \"align-justify\"\r\n | \"align-left\"\r\n | \"align-right\"\r\n | \"arrow-left\"\r\n | \"ban\"\r\n | \"blockquote\"\r\n | \"bold\"\r\n | \"chevron-down\"\r\n | \"code-block\"\r\n | \"code2\"\r\n | \"corner-down-left\"\r\n | \"eye\"\r\n | \"external-link\"\r\n | \"heading-five\"\r\n | \"heading-four\"\r\n | \"heading\"\r\n | \"heading-one\"\r\n | \"heading-six\"\r\n | \"heading-three\"\r\n | \"heading-two\"\r\n | \"highlighter\"\r\n | \"image-plus\"\r\n | \"italic\"\r\n | \"link\"\r\n | \"list\"\r\n | \"list-ordered\"\r\n | \"list-todo\"\r\n | \"redo2\"\r\n | \"strike\"\r\n | \"subscript\"\r\n | \"superscript\"\r\n | \"trash\"\r\n | \"underline\"\r\n | \"undo2\"\r\n | \"paperclip\"\r\n | \"youtube\"\r\n | \"table\"\r\n | \"file-text\"\r\n","import { defineCommand } from \"@/components/tiptap-ui/command\"\r\nimport { type Editor } from \"@tiptap/core\"\r\nimport { NodeSelection, TextSelection } from \"@tiptap/pm/state\"\r\n\r\n// --- Lib ---\r\nimport {\r\n findNodePosition,\r\n getSelectedBlockNodes,\r\n isNodeInSchema,\r\n isNodeTypeSelected,\r\n isValidPosition,\r\n selectionWithinConvertibleTypes,\r\n} from \"@/lib/tiptap-utils\"\r\n\r\n// --- Icons ---\r\nimport { HeadingOneIcon, HeadingTwoIcon, HeadingThreeIcon, HeadingFourIcon, HeadingFiveIcon, HeadingSixIcon } from \"@/components/tiptap-icons\"\r\n\r\nexport type Level = 1 | 2 | 3 | 4 | 5 | 6\r\n\r\nexport const headingIcons = {\r\n 1: HeadingOneIcon,\r\n 2: HeadingTwoIcon,\r\n 3: HeadingThreeIcon,\r\n 4: HeadingFourIcon,\r\n 5: HeadingFiveIcon,\r\n 6: HeadingSixIcon,\r\n}\r\n\r\nexport const HEADING_SHORTCUT_KEYS: Record<Level, string> = {\r\n 1: \"ctrl+alt+1\",\r\n 2: \"ctrl+alt+2\",\r\n 3: \"ctrl+alt+3\",\r\n 4: \"ctrl+alt+4\",\r\n 5: \"ctrl+alt+5\",\r\n 6: \"ctrl+alt+6\",\r\n}\r\n\r\n/**\r\n * Checks if heading can be toggled in the current editor state\r\n */\r\nexport function canToggle(\r\n editor: Editor | null,\r\n level?: Level,\r\n turnInto: boolean = true\r\n): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n if (\r\n !isNodeInSchema(\"heading\", editor) ||\r\n isNodeTypeSelected(editor, [\"image\"])\r\n )\r\n return false\r\n\r\n if (!turnInto) {\r\n return level\r\n ? editor.can().setNode(\"heading\", { level })\r\n : editor.can().setNode(\"heading\")\r\n }\r\n\r\n // Ensure selection is in nodes we're allowed to convert\r\n if (\r\n !selectionWithinConvertibleTypes(editor, [\r\n \"paragraph\",\r\n \"heading\",\r\n \"bulletList\",\r\n \"orderedList\",\r\n \"taskList\",\r\n \"blockquote\",\r\n \"codeBlock\",\r\n ])\r\n )\r\n return false\r\n\r\n // Either we can set heading directly on the selection,\r\n // or we can clear formatting/nodes to arrive at a heading.\r\n return level\r\n ? editor.can().setNode(\"heading\", { level }) || editor.can().clearNodes()\r\n : editor.can().setNode(\"heading\") || editor.can().clearNodes()\r\n}\r\n\r\n/**\r\n * Checks if heading is currently active\r\n */\r\nexport function isHeadingActive(\r\n editor: Editor | null,\r\n level?: Level | Level[]\r\n): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n\r\n if (Array.isArray(level)) {\r\n return level.some((l) => editor.isActive(\"heading\", { level: l }))\r\n }\r\n\r\n return level\r\n ? editor.isActive(\"heading\", { level })\r\n : editor.isActive(\"heading\")\r\n}\r\n\r\n/**\r\n * Toggles heading in the editor\r\n */\r\nexport function toggleHeading(\r\n editor: Editor | null,\r\n level: Level | Level[]\r\n): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n\r\n const levels = Array.isArray(level) ? level : [level]\r\n const toggleLevel = levels.find((l) => canToggle(editor, l))\r\n\r\n if (!toggleLevel) return false\r\n\r\n try {\r\n const view = editor.view\r\n let state = view.state\r\n let tr = state.tr\r\n\r\n const blocks = getSelectedBlockNodes(editor)\r\n\r\n // In case a selection contains multiple blocks, we only allow\r\n // toggling to node if there's exactly one block selected\r\n // we also dont block the canToggle since it will fall back to the bottom logic\r\n const isPossibleToTurnInto =\r\n selectionWithinConvertibleTypes(editor, [\r\n \"paragraph\",\r\n \"heading\",\r\n \"bulletList\",\r\n \"orderedList\",\r\n \"taskList\",\r\n \"blockquote\",\r\n \"codeBlock\",\r\n ]) && blocks.length === 1\r\n\r\n // No selection, find the the cursor position\r\n if (\r\n (state.selection.empty || state.selection instanceof TextSelection) &&\r\n isPossibleToTurnInto\r\n ) {\r\n const pos = findNodePosition({\r\n editor,\r\n node: state.selection.$anchor.node(1),\r\n })?.pos\r\n if (!isValidPosition(pos)) return false\r\n\r\n tr = tr.setSelection(NodeSelection.create(state.doc, pos))\r\n view.dispatch(tr)\r\n state = view.state\r\n }\r\n\r\n const selection = state.selection\r\n let chain = editor.chain().focus()\r\n\r\n // Handle NodeSelection\r\n if (selection instanceof NodeSelection) {\r\n const firstChild = selection.node.firstChild?.firstChild\r\n const lastChild = selection.node.lastChild?.lastChild\r\n\r\n const from = firstChild\r\n ? selection.from + firstChild.nodeSize\r\n : selection.from + 1\r\n\r\n const to = lastChild\r\n ? selection.to - lastChild.nodeSize\r\n : selection.to - 1\r\n\r\n const resolvedFrom = state.doc.resolve(from)\r\n const resolvedTo = state.doc.resolve(to)\r\n\r\n chain = chain\r\n .setTextSelection(TextSelection.between(resolvedFrom, resolvedTo))\r\n .clearNodes()\r\n }\r\n\r\n const isActive = levels.some((l) =>\r\n editor.isActive(\"heading\", { level: l })\r\n )\r\n\r\n const toggle = isActive\r\n ? chain.setNode(\"paragraph\")\r\n : chain.setNode(\"heading\", { level: toggleLevel })\r\n\r\n toggle.run()\r\n\r\n editor.chain().focus().selectTextblockEnd().run()\r\n\r\n return true\r\n } catch {\r\n return false\r\n }\r\n}\r\n\r\n\r\n/** p5: toolbar command, rendered by CommandButton (see ../command.ts) */\r\nexport const headingCommand = (level: Level) =>\r\n defineCommand(`heading:${level}`, () => ({\r\n label: (t) => t.heading.level(level),\r\n icon: headingIcons[level],\r\n shortcutKeys: HEADING_SHORTCUT_KEYS[level],\r\n isActive: (editor) => isHeadingActive(editor, level),\r\n canRun: (editor) => canToggle(editor, level),\r\n run: (editor) => toggleHeading(editor, level),\r\n }))\r\n","import type { Editor } from \"@tiptap/core\"\n\n// --- Tiptap UI ---\nimport { type Level, isHeadingActive } from \"@/components/tiptap-ui/heading-button\"\n\n/**\n * Gets the currently active heading level from the available levels\n */\nexport function getActiveHeadingLevel(\n editor: Editor | null,\n levels: Level[] = [1, 2, 3, 4, 5, 6]\n): Level | undefined {\n if (!editor || !editor.isEditable) return undefined\n return levels.find((level) => isHeadingActive(editor, level))\n}\n","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" style=\\\"display:none\\\">\\n<symbol id=\\\"tt-icon-align-center\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M2 6C2 5.44772 2.44772 5 3 5H21C21.5523 5 22 5.44772 22 6C22 6.55228 21.5523 7 21 7H3C2.44772 7 2 6.55228 2 6Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M6 12C6 11.4477 6.44772 11 7 11H17C17.5523 11 18 11.4477 18 12C18 12.5523 17.5523 13 17 13H7C6.44772 13 6 12.5523 6 12Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M4 18C4 17.4477 4.44772 17 5 17H19C19.5523 17 20 17.4477 20 18C20 18.5523 19.5523 19 19 19H5C4.44772 19 4 18.5523 4 18Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-align-justify\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M2 6C2 5.44772 2.44772 5 3 5H21C21.5523 5 22 5.44772 22 6C22 6.55228 21.5523 7 21 7H3C2.44772 7 2 6.55228 2 6Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M2 12C2 11.4477 2.44772 11 3 11H21C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13H3C2.44772 13 2 12.5523 2 12Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M2 18C2 17.4477 2.44772 17 3 17H21C21.5523 17 22 17.4477 22 18C22 18.5523 21.5523 19 21 19H3C2.44772 19 2 18.5523 2 18Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-align-left\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M2 6C2 5.44772 2.44772 5 3 5H21C21.5523 5 22 5.44772 22 6C22 6.55228 21.5523 7 21 7H3C2.44772 7 2 6.55228 2 6Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M2 12C2 11.4477 2.44772 11 3 11H15C15.5523 11 16 11.4477 16 12C16 12.5523 15.5523 13 15 13H3C2.44772 13 2 12.5523 2 12Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M2 18C2 17.4477 2.44772 17 3 17H17C17.5523 17 18 17.4477 18 18C18 18.5523 17.5523 19 17 19H3C2.44772 19 2 18.5523 2 18Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-align-right\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M2 6C2 5.44772 2.44772 5 3 5H21C21.5523 5 22 5.44772 22 6C22 6.55228 21.5523 7 21 7H3C2.44772 7 2 6.55228 2 6Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M8 12C8 11.4477 8.44772 11 9 11H21C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13H9C8.44772 13 8 12.5523 8 12Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M6 18C6 17.4477 6.44772 17 7 17H21C21.5523 17 22 17.4477 22 18C22 18.5523 21.5523 19 21 19H7C6.44772 19 6 18.5523 6 18Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-arrow-left\\\" viewBox=\\\"0 0 24 24\\\"><path d=\\\"M12.7071 5.70711C13.0976 5.31658 13.0976 4.68342 12.7071 4.29289C12.3166 3.90237 11.6834 3.90237 11.2929 4.29289L4.29289 11.2929C3.90237 11.6834 3.90237 12.3166 4.29289 12.7071L11.2929 19.7071C11.6834 20.0976 12.3166 20.0976 12.7071 19.7071C13.0976 19.3166 13.0976 18.6834 12.7071 18.2929L7.41421 13L19 13C19.5523 13 20 12.5523 20 12C20 11.4477 19.5523 11 19 11L7.41421 11L12.7071 5.70711Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-ban\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M4.43471 4.01458C4.34773 4.06032 4.26607 4.11977 4.19292 4.19292C4.11977 4.26607 4.06032 4.34773 4.01458 4.43471C2.14611 6.40628 1 9.0693 1 12C1 18.0751 5.92487 23 12 23C14.9306 23 17.5936 21.854 19.5651 19.9856C19.6522 19.9398 19.7339 19.8803 19.8071 19.8071C19.8803 19.7339 19.9398 19.6522 19.9856 19.5651C21.854 17.5936 23 14.9306 23 12C23 5.92487 18.0751 1 12 1C9.0693 1 6.40628 2.14611 4.43471 4.01458ZM6.38231 4.9681C7.92199 3.73647 9.87499 3 12 3C16.9706 3 21 7.02944 21 12C21 14.125 20.2635 16.078 19.0319 17.6177L6.38231 4.9681ZM17.6177 19.0319C16.078 20.2635 14.125 21 12 21C7.02944 21 3 16.9706 3 12C3 9.87499 3.73647 7.92199 4.9681 6.38231L17.6177 19.0319Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-blockquote\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M8 6C8 5.44772 8.44772 5 9 5H16C16.5523 5 17 5.44772 17 6C17 6.55228 16.5523 7 16 7H9C8.44772 7 8 6.55228 8 6Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M4 3C4.55228 3 5 3.44772 5 4L5 20C5 20.5523 4.55229 21 4 21C3.44772 21 3 20.5523 3 20L3 4C3 3.44772 3.44772 3 4 3Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M8 12C8 11.4477 8.44772 11 9 11H20C20.5523 11 21 11.4477 21 12C21 12.5523 20.5523 13 20 13H9C8.44772 13 8 12.5523 8 12Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M8 18C8 17.4477 8.44772 17 9 17H16C16.5523 17 17 17.4477 17 18C17 18.5523 16.5523 19 16 19H9C8.44772 19 8 18.5523 8 18Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-bold\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M6 2.5C5.17157 2.5 4.5 3.17157 4.5 4V20C4.5 20.8284 5.17157 21.5 6 21.5H15C16.4587 21.5 17.8576 20.9205 18.8891 19.8891C19.9205 18.8576 20.5 17.4587 20.5 16C20.5 14.5413 19.9205 13.1424 18.8891 12.1109C18.6781 11.9 18.4518 11.7079 18.2128 11.5359C19.041 10.5492 19.5 9.29829 19.5 8C19.5 6.54131 18.9205 5.14236 17.8891 4.11091C16.8576 3.07946 15.4587 2.5 14 2.5H6ZM14 10.5C14.663 10.5 15.2989 10.2366 15.7678 9.76777C16.2366 9.29893 16.5 8.66304 16.5 8C16.5 7.33696 16.2366 6.70107 15.7678 6.23223C15.2989 5.76339 14.663 5.5 14 5.5H7.5V10.5H14ZM7.5 18.5V13.5H15C15.663 13.5 16.2989 13.7634 16.7678 14.2322C17.2366 14.7011 17.5 15.337 17.5 16C17.5 16.663 17.2366 17.2989 16.7678 17.7678C16.2989 18.2366 15.663 18.5 15 18.5H7.5Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-chevron-down\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M5.29289 8.29289C5.68342 7.90237 6.31658 7.90237 6.70711 8.29289L12 13.5858L17.2929 8.29289C17.6834 7.90237 18.3166 7.90237 18.7071 8.29289C19.0976 8.68342 19.0976 9.31658 18.7071 9.70711L12.7071 15.7071C12.3166 16.0976 11.6834 16.0976 11.2929 15.7071L5.29289 9.70711C4.90237 9.31658 4.90237 8.68342 5.29289 8.29289Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-code-block\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M6.70711 2.29289C7.09763 2.68342 7.09763 3.31658 6.70711 3.70711L4.41421 6L6.70711 8.29289C7.09763 8.68342 7.09763 9.31658 6.70711 9.70711C6.31658 10.0976 5.68342 10.0976 5.29289 9.70711L2.29289 6.70711C1.90237 6.31658 1.90237 5.68342 2.29289 5.29289L5.29289 2.29289C5.68342 1.90237 6.31658 1.90237 6.70711 2.29289Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M10.2929 2.29289C10.6834 1.90237 11.3166 1.90237 11.7071 2.29289L14.7071 5.29289C15.0976 5.68342 15.0976 6.31658 14.7071 6.70711L11.7071 9.70711C11.3166 10.0976 10.6834 10.0976 10.2929 9.70711C9.90237 9.31658 9.90237 8.68342 10.2929 8.29289L12.5858 6L10.2929 3.70711C9.90237 3.31658 9.90237 2.68342 10.2929 2.29289Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M17 4C17 3.44772 17.4477 3 18 3H19C20.6569 3 22 4.34315 22 6V18C22 19.6569 20.6569 21 19 21H5C3.34315 21 2 19.6569 2 18V12C2 11.4477 2.44772 11 3 11C3.55228 11 4 11.4477 4 12V18C4 18.5523 4.44772 19 5 19H19C19.5523 19 20 18.5523 20 18V6C20 5.44772 19.5523 5 19 5H18C17.4477 5 17 4.55228 17 4Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-code2\\\" viewBox=\\\"0 0 24 24\\\"><path d=\\\"M15.4545 4.2983C15.6192 3.77115 15.3254 3.21028 14.7983 3.04554C14.2712 2.88081 13.7103 3.1746 13.5455 3.70175L8.54554 19.7017C8.38081 20.2289 8.6746 20.7898 9.20175 20.9545C9.72889 21.1192 10.2898 20.8254 10.4545 20.2983L15.4545 4.2983Z\\\" fill=\\\"currentColor\\\" /><path d=\\\"M6.70711 7.29289C7.09763 7.68342 7.09763 8.31658 6.70711 8.70711L3.41421 12L6.70711 15.2929C7.09763 15.6834 7.09763 16.3166 6.70711 16.7071C6.31658 17.0976 5.68342 17.0976 5.29289 16.7071L1.29289 12.7071C0.902369 12.3166 0.902369 11.6834 1.29289 11.2929L5.29289 7.29289C5.68342 6.90237 6.31658 6.90237 6.70711 7.29289Z\\\" fill=\\\"currentColor\\\" /><path d=\\\"M17.2929 7.29289C17.6834 6.90237 18.3166 6.90237 18.7071 7.29289L22.7071 11.2929C23.0976 11.6834 23.0976 12.3166 22.7071 12.7071L18.7071 16.7071C18.3166 17.0976 17.6834 17.0976 17.2929 16.7071C16.9024 16.3166 16.9024 15.6834 17.2929 15.2929L20.5858 12L17.2929 8.70711C16.9024 8.31658 16.9024 7.68342 17.2929 7.29289Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-corner-down-left\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M21 4C21 3.44772 20.5523 3 20 3C19.4477 3 19 3.44772 19 4V11C19 11.7956 18.6839 12.5587 18.1213 13.1213C17.5587 13.6839 16.7956 14 16 14H6.41421L9.70711 10.7071C10.0976 10.3166 10.0976 9.68342 9.70711 9.29289C9.31658 8.90237 8.68342 8.90237 8.29289 9.29289L3.29289 14.2929C2.90237 14.6834 2.90237 15.3166 3.29289 15.7071L8.29289 20.7071C8.68342 21.0976 9.31658 21.0976 9.70711 20.7071C10.0976 20.3166 10.0976 19.6834 9.70711 19.2929L6.41421 16H16C17.3261 16 18.5979 15.4732 19.5355 14.5355C20.4732 13.5979 21 12.3261 21 11V4Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-eye\\\" viewBox=\\\"0 0 24 24\\\"><path d=\\\"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" /><circle cx=\\\"12\\\" cy=\\\"12\\\" r=\\\"3\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" /></symbol>\\n<symbol id=\\\"tt-icon-external-link\\\" viewBox=\\\"0 0 24 24\\\"><path d=\\\"M14 3C14 2.44772 14.4477 2 15 2H21C21.5523 2 22 2.44772 22 3V9C22 9.55228 21.5523 10 21 10C20.4477 10 20 9.55228 20 9V5.41421L10.7071 14.7071C10.3166 15.0976 9.68342 15.0976 9.29289 14.7071C8.90237 14.3166 8.90237 13.6834 9.29289 13.2929L18.5858 4H15C14.4477 4 14 3.55228 14 3Z\\\" fill=\\\"currentColor\\\" /><path d=\\\"M4.29289 7.29289C4.48043 7.10536 4.73478 7 5 7H11C11.5523 7 12 6.55228 12 6C12 5.44772 11.5523 5 11 5H5C4.20435 5 3.44129 5.31607 2.87868 5.87868C2.31607 6.44129 2 7.20435 2 8V19C2 19.7957 2.31607 20.5587 2.87868 21.1213C3.44129 21.6839 4.20435 22 5 22H16C16.7957 22 17.5587 21.6839 18.1213 21.1213C18.6839 20.5587 19 19.7957 19 19V13C19 12.4477 18.5523 12 18 12C17.4477 12 17 12.4477 17 13V19C17 19.2652 16.8946 19.5196 16.7071 19.7071C16.5196 19.8946 16.2652 20 16 20H5C4.73478 20 4.48043 19.8946 4.29289 19.7071C4.10536 19.5196 4 19.2652 4 19V8C4 7.73478 4.10536 7.48043 4.29289 7.29289Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-heading-five\\\" viewBox=\\\"0 0 24 24\\\"><path 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\\\" fill=\\\"currentColor\\\" /><path 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\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-heading-four\\\" viewBox=\\\"0 0 24 24\\\"><path 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\\\" fill=\\\"currentColor\\\" /><path 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\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-heading\\\" viewBox=\\\"0 0 24 24\\\"><path 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\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-heading-one\\\" viewBox=\\\"0 0 24 24\\\"><path 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\\\" fill=\\\"currentColor\\\" /><path 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\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-heading-six\\\" viewBox=\\\"0 0 24 24\\\"><path 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\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" 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\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-heading-three\\\" viewBox=\\\"0 0 24 24\\\"><path 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\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" 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\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" 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\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-heading-two\\\" viewBox=\\\"0 0 24 24\\\"><path 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\\\" fill=\\\"currentColor\\\" /><path 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\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-highlighter\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M14.7072 4.70711C15.0977 4.31658 15.0977 3.68342 14.7072 3.29289C14.3167 2.90237 13.6835 2.90237 13.293 3.29289L8.69294 7.89286L8.68594 7.9C8.13626 8.46079 7.82837 9.21474 7.82837 10C7.82837 10.2306 7.85491 10.4584 7.90631 10.6795L2.29289 16.2929C2.10536 16.4804 2 16.7348 2 17V20C2 20.5523 2.44772 21 3 21H12C12.2652 21 12.5196 20.8946 12.7071 20.7071L15.3205 18.0937C15.5416 18.1452 15.7695 18.1717 16.0001 18.1717C16.7853 18.1717 17.5393 17.8639 18.1001 17.3142L22.7072 12.7071C23.0977 12.3166 23.0977 11.6834 22.7072 11.2929C22.3167 10.9024 21.6835 10.9024 21.293 11.2929L16.6971 15.8887C16.5105 16.0702 16.2605 16.1717 16.0001 16.1717C15.7397 16.1717 15.4897 16.0702 15.303 15.8887L10.1113 10.697C9.92992 10.5104 9.82837 10.2604 9.82837 10C9.82837 9.73963 9.92992 9.48958 10.1113 9.30297L14.7072 4.70711ZM13.5858 17L9.00004 12.4142L4 17.4142V19H11.5858L13.5858 17Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-image-plus\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M20 2C20 1.44772 19.5523 1 19 1C18.4477 1 18 1.44772 18 2V4H16C15.4477 4 15 4.44772 15 5C15 5.55228 15.4477 6 16 6H18V8C18 8.55228 18.4477 9 19 9C19.5523 9 20 8.55228 20 8V6H22C22.5523 6 23 5.55228 23 5C23 4.44772 22.5523 4 22 4H20V2ZM5 4C4.73478 4 4.48043 4.10536 4.29289 4.29289C4.10536 4.48043 4 4.73478 4 5V19C4 19.2652 4.10536 19.5196 4.29289 19.7071C4.48043 19.8946 4.73478 20 5 20H5.58579L14.379 11.2068C14.9416 10.6444 15.7045 10.3284 16.5 10.3284C17.2955 10.3284 18.0584 10.6444 18.621 11.2068L20 12.5858V12C20 11.4477 20.4477 11 21 11C21.5523 11 22 11.4477 22 12V14.998C22 14.9994 22 15.0007 22 15.002V19C22 19.7957 21.6839 20.5587 21.1213 21.1213C20.5587 21.6839 19.7957 22 19 22H6.00219C6.00073 22 5.99927 22 5.99781 22H5C4.20435 22 3.44129 21.6839 2.87868 21.1213C2.31607 20.5587 2 19.7957 2 19V5C2 4.20435 2.31607 3.44129 2.87868 2.87868C3.44129 2.31607 4.20435 2 5 2H12C12.5523 2 13 2.44772 13 3C13 3.55228 12.5523 4 12 4H5ZM8.41422 20H19C19.2652 20 19.5196 19.8946 19.7071 19.7071C19.8946 19.5196 20 19.2652 20 19V15.4142L17.207 12.6212C17.0195 12.4338 16.7651 12.3284 16.5 12.3284C16.2349 12.3284 15.9806 12.4337 15.7931 12.6211L8.41422 20ZM6.87868 6.87868C7.44129 6.31607 8.20435 6 9 6C9.79565 6 10.5587 6.31607 11.1213 6.87868C11.6839 7.44129 12 8.20435 12 9C12 9.79565 11.6839 10.5587 11.1213 11.1213C10.5587 11.6839 9.79565 12 9 12C8.20435 12 7.44129 11.6839 6.87868 11.1213C6.31607 10.5587 6 9.79565 6 9C6 8.20435 6.31607 7.44129 6.87868 6.87868ZM9 8C8.73478 8 8.48043 8.10536 8.29289 8.29289C8.10536 8.48043 8 8.73478 8 9C8 9.26522 8.10536 9.51957 8.29289 9.70711C8.48043 9.89464 8.73478 10 9 10C9.26522 10 9.51957 9.89464 9.70711 9.70711C9.89464 9.51957 10 9.26522 10 9C10 8.73478 9.89464 8.48043 9.70711 8.29289C9.51957 8.10536 9.26522 8 9 8Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-italic\\\" viewBox=\\\"0 0 24 24\\\"><path d=\\\"M15.0222 3H19C19.5523 3 20 3.44772 20 4C20 4.55228 19.5523 5 19 5H15.693L10.443 19H14C14.5523 19 15 19.4477 15 20C15 20.5523 14.5523 21 14 21H9.02418C9.00802 21.0004 8.99181 21.0004 8.97557 21H5C4.44772 21 4 20.5523 4 20C4 19.4477 4.44772 19 5 19H8.30704L13.557 5H10C9.44772 5 9 4.55228 9 4C9 3.44772 9.44772 3 10 3H14.9782C14.9928 2.99968 15.0075 2.99967 15.0222 3Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-link\\\" viewBox=\\\"0 0 24 24\\\"><path d=\\\"M16.9958 1.06669C15.4226 1.05302 13.907 1.65779 12.7753 2.75074L12.765 2.76086L11.045 4.47086C10.6534 4.86024 10.6515 5.49341 11.0409 5.88507C11.4303 6.27673 12.0634 6.27858 12.4551 5.88919L14.1697 4.18456C14.9236 3.45893 15.9319 3.05752 16.9784 3.06662C18.0272 3.07573 19.0304 3.49641 19.772 4.23804C20.5137 4.97967 20.9344 5.98292 20.9435 7.03171C20.9526 8.07776 20.5515 9.08563 19.8265 9.83941L16.833 12.8329C16.4274 13.2386 15.9393 13.5524 15.4019 13.7529C14.8645 13.9533 14.2903 14.0359 13.7181 13.9949C13.146 13.9539 12.5894 13.7904 12.0861 13.5154C11.5827 13.2404 11.1444 12.8604 10.8008 12.401C10.47 11.9588 9.84333 11.8685 9.40108 12.1993C8.95883 12.5301 8.86849 13.1568 9.1993 13.599C9.71464 14.288 10.3721 14.858 11.1272 15.2705C11.8822 15.683 12.7171 15.9283 13.5753 15.9898C14.4334 16.0513 15.2948 15.9274 16.1009 15.6267C16.907 15.326 17.639 14.8555 18.2473 14.247L21.2472 11.2471L21.2593 11.2347C22.3523 10.1031 22.9571 8.58751 22.9434 7.01433C22.9297 5.44115 22.2987 3.93628 21.1863 2.82383C20.0738 1.71138 18.5689 1.08036 16.9958 1.06669Z\\\" fill=\\\"currentColor\\\" /><path d=\\\"M10.4247 8.0102C9.56657 7.94874 8.70522 8.07256 7.89911 8.37326C7.09305 8.67395 6.36096 9.14458 5.75272 9.753L2.75285 12.7529L2.74067 12.7653C1.64772 13.8969 1.04295 15.4125 1.05662 16.9857C1.07029 18.5589 1.70131 20.0637 2.81376 21.1762C3.9262 22.2886 5.43108 22.9196 7.00426 22.9333C8.57744 22.947 10.0931 22.3422 11.2247 21.2493L11.2371 21.2371L12.9471 19.5271C13.3376 19.1366 13.3376 18.5034 12.9471 18.1129C12.5565 17.7223 11.9234 17.7223 11.5328 18.1129L9.82932 19.8164C9.07555 20.5414 8.06768 20.9425 7.02164 20.9334C5.97285 20.9243 4.9696 20.5036 4.22797 19.762C3.48634 19.0203 3.06566 18.0171 3.05655 16.9683C3.04746 15.9222 3.44851 14.9144 4.17355 14.1606L7.16719 11.167C7.5727 10.7613 8.06071 10.4476 8.59811 10.2471C9.13552 10.0467 9.70976 9.96412 10.2819 10.0051C10.854 10.0461 11.4106 10.2096 11.9139 10.4846C12.4173 10.7596 12.8556 11.1397 13.1992 11.599C13.53 12.0412 14.1567 12.1316 14.5989 11.8007C15.0412 11.4699 15.1315 10.8433 14.8007 10.401C14.2854 9.71205 13.6279 9.14198 12.8729 8.72948C12.1178 8.31697 11.2829 8.07166 10.4247 8.0102Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-list\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M7 6C7 5.44772 7.44772 5 8 5H21C21.5523 5 22 5.44772 22 6C22 6.55228 21.5523 7 21 7H8C7.44772 7 7 6.55228 7 6Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M7 12C7 11.4477 7.44772 11 8 11H21C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13H8C7.44772 13 7 12.5523 7 12Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M7 18C7 17.4477 7.44772 17 8 17H21C21.5523 17 22 17.4477 22 18C22 18.5523 21.5523 19 21 19H8C7.44772 19 7 18.5523 7 18Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M2 6C2 5.44772 2.44772 5 3 5H3.01C3.56228 5 4.01 5.44772 4.01 6C4.01 6.55228 3.56228 7 3.01 7H3C2.44772 7 2 6.55228 2 6Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M2 12C2 11.4477 2.44772 11 3 11H3.01C3.56228 11 4.01 11.4477 4.01 12C4.01 12.5523 3.56228 13 3.01 13H3C2.44772 13 2 12.5523 2 12Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M2 18C2 17.4477 2.44772 17 3 17H3.01C3.56228 17 4.01 17.4477 4.01 18C4.01 18.5523 3.56228 19 3.01 19H3C2.44772 19 2 18.5523 2 18Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-list-ordered\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M9 6C9 5.44772 9.44772 5 10 5H21C21.5523 5 22 5.44772 22 6C22 6.55228 21.5523 7 21 7H10C9.44772 7 9 6.55228 9 6Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M9 12C9 11.4477 9.44772 11 10 11H21C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13H10C9.44772 13 9 12.5523 9 12Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M9 18C9 17.4477 9.44772 17 10 17H21C21.5523 17 22 17.4477 22 18C22 18.5523 21.5523 19 21 19H10C9.44772 19 9 18.5523 9 18Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M3 6C3 5.44772 3.44772 5 4 5H5C5.55228 5 6 5.44772 6 6V10C6 10.5523 5.55228 11 5 11C4.44772 11 4 10.5523 4 10V7C3.44772 7 3 6.55228 3 6Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M3 10C3 9.44772 3.44772 9 4 9H6C6.55228 9 7 9.44772 7 10C7 10.5523 6.55228 11 6 11H4C3.44772 11 3 10.5523 3 10Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M5.82219 13.0431C6.54543 13.4047 6.99997 14.1319 6.99997 15C6.99997 15.5763 6.71806 16.0426 6.48747 16.35C6.31395 16.5814 6.1052 16.8044 5.91309 17H5.99997C6.55226 17 6.99997 17.4477 6.99997 18C6.99997 18.5523 6.55226 19 5.99997 19H3.99997C3.44769 19 2.99997 18.5523 2.99997 18C2.99997 17.4237 3.28189 16.9575 3.51247 16.65C3.74323 16.3424 4.03626 16.0494 4.26965 15.8161C4.27745 15.8083 4.2852 15.8006 4.29287 15.7929C4.55594 15.5298 4.75095 15.3321 4.88748 15.15C4.96287 15.0495 4.99021 14.9922 4.99911 14.9714C4.99535 14.9112 4.9803 14.882 4.9739 14.8715C4.96613 14.8588 4.95382 14.845 4.92776 14.8319C4.87723 14.8067 4.71156 14.7623 4.44719 14.8944C3.95321 15.1414 3.35254 14.9412 3.10555 14.4472C2.85856 13.9533 3.05878 13.3526 3.55276 13.1056C4.28839 12.7378 5.12272 12.6934 5.82219 13.0431Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-list-todo\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" 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\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" 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\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" 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\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" 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\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" 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\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-redo2\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M15.7071 2.29289C15.3166 1.90237 14.6834 1.90237 14.2929 2.29289C13.9024 2.68342 13.9024 3.31658 14.2929 3.70711L17.5858 7H9.5C7.77609 7 6.12279 7.68482 4.90381 8.90381C3.68482 10.1228 3 11.7761 3 13.5C3 14.3536 3.16813 15.1988 3.49478 15.9874C3.82144 16.7761 4.30023 17.4926 4.90381 18.0962C6.12279 19.3152 7.77609 20 9.5 20H13C13.5523 20 14 19.5523 14 19C14 18.4477 13.5523 18 13 18H9.5C8.30653 18 7.16193 17.5259 6.31802 16.682C5.90016 16.2641 5.56869 15.768 5.34254 15.2221C5.1164 14.6761 5 14.0909 5 13.5C5 12.3065 5.47411 11.1619 6.31802 10.318C7.16193 9.47411 8.30653 9 9.5 9H17.5858L14.2929 12.2929C13.9024 12.6834 13.9024 13.3166 14.2929 13.7071C14.6834 14.0976 15.3166 14.0976 15.7071 13.7071L20.7071 8.70711C21.0976 8.31658 21.0976 7.68342 20.7071 7.29289L15.7071 2.29289Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-strike\\\" viewBox=\\\"0 0 24 24\\\"><path d=\\\"M9.00039 3H16.0001C16.5524 3 17.0001 3.44772 17.0001 4C17.0001 4.55229 16.5524 5 16.0001 5H9.00011C8.68006 4.99983 8.36412 5.07648 8.07983 5.22349C7.79555 5.37051 7.55069 5.5836 7.36585 5.84487C7.181 6.10614 7.06155 6.40796 7.01754 6.72497C6.97352 7.04198 7.00623 7.36492 7.11292 7.66667C7.29701 8.18737 7.02414 8.75872 6.50344 8.94281C5.98274 9.1269 5.4114 8.85403 5.2273 8.33333C5.01393 7.72984 4.94851 7.08396 5.03654 6.44994C5.12456 5.81592 5.36346 5.21229 5.73316 4.68974C6.10285 4.1672 6.59256 3.74101 7.16113 3.44698C7.72955 3.15303 8.36047 2.99975 9.00039 3Z\\\" fill=\\\"currentColor\\\" /><path d=\\\"M18 13H20C20.5523 13 21 12.5523 21 12C21 11.4477 20.5523 11 20 11H4C3.44772 11 3 11.4477 3 12C3 12.5523 3.44772 13 4 13H14C14.7956 13 15.5587 13.3161 16.1213 13.8787C16.6839 14.4413 17 15.2044 17 16C17 16.7956 16.6839 17.5587 16.1213 18.1213C15.5587 18.6839 14.7956 19 14 19H6C5.44772 19 5 19.4477 5 20C5 20.5523 5.44772 21 6 21H14C15.3261 21 16.5979 20.4732 17.5355 19.5355C18.4732 18.5979 19 17.3261 19 16C19 14.9119 18.6453 13.8604 18 13Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-subscript\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M3.29289 7.29289C3.68342 6.90237 4.31658 6.90237 4.70711 7.29289L12.7071 15.2929C13.0976 15.6834 13.0976 16.3166 12.7071 16.7071C12.3166 17.0976 11.6834 17.0976 11.2929 16.7071L3.29289 8.70711C2.90237 8.31658 2.90237 7.68342 3.29289 7.29289Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M12.7071 7.29289C13.0976 7.68342 13.0976 8.31658 12.7071 8.70711L4.70711 16.7071C4.31658 17.0976 3.68342 17.0976 3.29289 16.7071C2.90237 16.3166 2.90237 15.6834 3.29289 15.2929L11.2929 7.29289C11.6834 6.90237 12.3166 6.90237 12.7071 7.29289Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M17.4079 14.3995C18.0284 14.0487 18.7506 13.9217 19.4536 14.0397C20.1566 14.1578 20.7977 14.5138 21.2696 15.0481L21.2779 15.0574L21.2778 15.0575C21.7439 15.5988 22 16.2903 22 17C22 18.0823 21.3962 18.8401 20.7744 19.3404C20.194 19.8073 19.4858 20.141 18.9828 20.378C18.9638 20.387 18.9451 20.3958 18.9266 20.4045C18.4473 20.6306 18.2804 20.7817 18.1922 20.918C18.1773 20.9412 18.1619 20.9681 18.1467 21H21C21.5523 21 22 21.4477 22 22C22 22.5523 21.5523 23 21 23H17C16.4477 23 16 22.5523 16 22C16 21.1708 16.1176 20.4431 16.5128 19.832C16.9096 19.2184 17.4928 18.8695 18.0734 18.5956C18.6279 18.334 19.138 18.0901 19.5207 17.7821C19.8838 17.49 20 17.2477 20 17C20 16.7718 19.9176 16.5452 19.7663 16.3672C19.5983 16.1792 19.3712 16.0539 19.1224 16.0121C18.8722 15.9701 18.6152 16.015 18.3942 16.1394C18.1794 16.2628 18.0205 16.4549 17.9422 16.675C17.7572 17.1954 17.1854 17.4673 16.665 17.2822C16.1446 17.0972 15.8728 16.5254 16.0578 16.005C16.2993 15.3259 16.7797 14.7584 17.4039 14.4018L17.4079 14.3995L17.4079 14.3995Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-superscript\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M12.7071 7.29289C13.0976 7.68342 13.0976 8.31658 12.7071 8.70711L4.70711 16.7071C4.31658 17.0976 3.68342 17.0976 3.29289 16.7071C2.90237 16.3166 2.90237 15.6834 3.29289 15.2929L11.2929 7.29289C11.6834 6.90237 12.3166 6.90237 12.7071 7.29289Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M3.29289 7.29289C3.68342 6.90237 4.31658 6.90237 4.70711 7.29289L12.7071 15.2929C13.0976 15.6834 13.0976 16.3166 12.7071 16.7071C12.3166 17.0976 11.6834 17.0976 11.2929 16.7071L3.29289 8.70711C2.90237 8.31658 2.90237 7.68342 3.29289 7.29289Z\\\" fill=\\\"currentColor\\\" /><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M17.405 1.40657C18.0246 1.05456 18.7463 0.92634 19.4492 1.04344C20.1521 1.16054 20.7933 1.51583 21.2652 2.0497L21.2697 2.05469L21.2696 2.05471C21.7431 2.5975 22 3.28922 22 4.00203C22 5.08579 21.3952 5.84326 20.7727 6.34289C20.1966 6.80531 19.4941 7.13675 18.9941 7.37261C18.9714 7.38332 18.9491 7.39383 18.9273 7.40415C18.4487 7.63034 18.2814 7.78152 18.1927 7.91844C18.1778 7.94155 18.1625 7.96834 18.1473 8.00003H21C21.5523 8.00003 22 8.44774 22 9.00003C22 9.55231 21.5523 10 21 10H17C16.4477 10 16 9.55231 16 9.00003C16 8.17007 16.1183 7.44255 16.5138 6.83161C16.9107 6.21854 17.4934 5.86971 18.0728 5.59591C18.6281 5.33347 19.1376 5.09075 19.5208 4.78316C19.8838 4.49179 20 4.25026 20 4.00203C20 3.77192 19.9178 3.54865 19.7646 3.37182C19.5968 3.18324 19.3696 3.05774 19.1205 3.01625C18.8705 2.97459 18.6137 3.02017 18.3933 3.14533C18.1762 3.26898 18.0191 3.45826 17.9406 3.67557C17.7531 4.19504 17.18 4.46414 16.6605 4.27662C16.141 4.0891 15.8719 3.51596 16.0594 2.99649C16.303 2.3219 16.7817 1.76125 17.4045 1.40689L17.405 1.40657Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-trash\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M7 5V4C7 3.17477 7.40255 2.43324 7.91789 1.91789C8.43324 1.40255 9.17477 1 10 1H14C14.8252 1 15.5668 1.40255 16.0821 1.91789C16.5975 2.43324 17 3.17477 17 4V5H21C21.5523 5 22 5.44772 22 6C22 6.55228 21.5523 7 21 7H20V20C20 20.8252 19.5975 21.5668 19.0821 22.0821C18.5668 22.5975 17.8252 23 17 23H7C6.17477 23 5.43324 22.5975 4.91789 22.0821C4.40255 21.5668 4 20.8252 4 20V7H3C2.44772 7 2 6.55228 2 6C2 5.44772 2.44772 5 3 5H7ZM9 4C9 3.82523 9.09745 3.56676 9.33211 3.33211C9.56676 3.09745 9.82523 3 10 3H14C14.1748 3 14.4332 3.09745 14.6679 3.33211C14.9025 3.56676 15 3.82523 15 4V5H9V4ZM6 7V20C6 20.1748 6.09745 20.4332 6.33211 20.6679C6.56676 20.9025 6.82523 21 7 21H17C17.1748 21 17.4332 20.9025 17.6679 20.6679C17.9025 20.4332 18 20.1748 18 20V7H6Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-underline\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M7 4C7 3.44772 6.55228 3 6 3C5.44772 3 5 3.44772 5 4V10C5 11.8565 5.7375 13.637 7.05025 14.9497C8.36301 16.2625 10.1435 17 12 17C13.8565 17 15.637 16.2625 16.9497 14.9497C18.2625 13.637 19 11.8565 19 10V4C19 3.44772 18.5523 3 18 3C17.4477 3 17 3.44772 17 4V10C17 11.3261 16.4732 12.5979 15.5355 13.5355C14.5979 14.4732 13.3261 15 12 15C10.6739 15 9.40215 14.4732 8.46447 13.5355C7.52678 12.5979 7 11.3261 7 10V4ZM4 19C3.44772 19 3 19.4477 3 20C3 20.5523 3.44772 21 4 21H20C20.5523 21 21 20.5523 21 20C21 19.4477 20.5523 19 20 19H4Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-undo2\\\" viewBox=\\\"0 0 24 24\\\"><path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M9.70711 3.70711C10.0976 3.31658 10.0976 2.68342 9.70711 2.29289C9.31658 1.90237 8.68342 1.90237 8.29289 2.29289L3.29289 7.29289C2.90237 7.68342 2.90237 8.31658 3.29289 8.70711L8.29289 13.7071C8.68342 14.0976 9.31658 14.0976 9.70711 13.7071C10.0976 13.3166 10.0976 12.6834 9.70711 12.2929L6.41421 9H14.5C15.0909 9 15.6761 9.1164 16.2221 9.34254C16.768 9.56869 17.2641 9.90016 17.682 10.318C18.0998 10.7359 18.4313 11.232 18.6575 11.7779C18.8836 12.3239 19 12.9091 19 13.5C19 14.0909 18.8836 14.6761 18.6575 15.2221C18.4313 15.768 18.0998 16.2641 17.682 16.682C17.2641 17.0998 16.768 17.4313 16.2221 17.6575C15.6761 17.8836 15.0909 18 14.5 18H11C10.4477 18 10 18.4477 10 19C10 19.5523 10.4477 20 11 20H14.5C15.3536 20 16.1988 19.8319 16.9874 19.5052C17.7761 19.1786 18.4926 18.6998 19.0962 18.0962C19.6998 17.4926 20.1786 16.7761 20.5052 15.9874C20.8319 15.1988 21 14.3536 21 13.5C21 12.6464 20.8319 11.8012 20.5052 11.0126C20.1786 10.2239 19.6998 9.50739 19.0962 8.90381C18.4926 8.30022 17.7761 7.82144 16.9874 7.49478C16.1988 7.16813 15.3536 7 14.5 7H6.41421L9.70711 3.70711Z\\\" fill=\\\"currentColor\\\" /></symbol>\\n<symbol id=\\\"tt-icon-paperclip\\\" viewBox=\\\"0 0 24 24\\\"><path d=\\\"m16 6-8.414 8.586a2 2 0 0 0 2.829 2.829l8.414-8.586a4 4 0 1 0-5.657-5.657l-8.379 8.551a6 6 0 1 0 8.485 8.485l8.379-8.551\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" /></symbol>\\n<symbol id=\\\"tt-icon-youtube\\\" viewBox=\\\"0 0 24 24\\\"><path d=\\\"M2.5 17a24.12 24.12 0 0 1 0-10 2 2 0 0 1 1.4-1.4 49.56 49.56 0 0 1 16.2 0A2 2 0 0 1 21.5 7a24.12 24.12 0 0 1 0 10 2 2 0 0 1-1.4 1.4 49.55 49.55 0 0 1-16.2 0A2 2 0 0 1 2.5 17\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" /><path d=\\\"m10 15 5-3-5-3z\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" /></symbol>\\n<symbol id=\\\"tt-icon-table\\\" viewBox=\\\"0 0 24 24\\\"><path d=\\\"M12 3v18\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" /><rect width=\\\"18\\\" height=\\\"18\\\" x=\\\"3\\\" y=\\\"3\\\" rx=\\\"2\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" /><path d=\\\"M3 9h18\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" /><path d=\\\"M3 15h18\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" /></symbol>\\n<symbol id=\\\"tt-icon-file-text\\\" viewBox=\\\"0 0 24 24\\\"><path d=\\\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" /><path d=\\\"M14 2v4a2 2 0 0 0 2 2h4\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" /><path d=\\\"M10 9H8\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" /><path d=\\\"M16 13H8\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" /><path d=\\\"M16 17H8\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" /></symbol>\\n</svg>\\n\"","<template>\n <svg width=\"24\" height=\"24\" aria-hidden=\"true\" focusable=\"false\">\n <use :href=\"`#tt-icon-${name}`\" />\n </svg>\n</template>\n\n<script setup lang=\"ts\">\nimport sprite from \"@/components/tiptap-icons/sprite.svg?raw\"\nimport type { IconName } from \"@/components/tiptap-icons\"\n\ndefineProps<{ name: IconName }>()\n\n// The sprite is added to the page once, the first time any icon renders.\n// Inlining it (rather than <use href=\"sprite.svg#id\">) keeps it working when the\n// package is consumed from another origin, and lets icons inherit currentColor.\nconst SPRITE_ID = \"tiptap-icon-sprite\"\nif (typeof document !== \"undefined\" && !document.getElementById(SPRITE_ID)) {\n const container = document.createElement(\"div\")\n container.id = SPRITE_ID\n container.hidden = true\n container.innerHTML = sprite\n document.body.prepend(container)\n}\n</script>\n","<template>\r\n <Button\r\n type=\"button\"\r\n role=\"button\"\r\n :tabindex=\"-1\"\r\n :disabled=\"!canRun\"\r\n :data-disabled=\"!canRun\"\r\n :data-active-state=\"isActive ? 'on' : 'off'\"\r\n :aria-pressed=\"command.isActive ? String(isActive) : undefined\"\r\n :aria-label=\"command.label(t)\"\r\n :tooltip=\"command.label(t)\"\r\n :shortcut-keys=\"command.shortcutKeys\"\r\n @click=\"command.run(editor)\"\r\n >\r\n <slot>\r\n <Icon :name=\"command.icon\" class=\"tiptap-button-icon\" />\r\n <span v-if=\"text\" class=\"tiptap-button-text\">{{ text }}</span>\r\n </slot>\r\n </Button>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { useTranslations } from \"@/composables/use-translations\"\r\nimport type { Editor } from \"@tiptap/core\"\r\nimport { useTiptapEditor } from \"@/composables/use-tiptap-editor\"\r\nimport { useEditorState, toStateRefs } from \"@/composables/use-editor-state\"\r\nimport { Button } from \"@/components/tiptap-ui-primitive/button\"\r\nimport Icon from \"@/components/tiptap-icons/Icon.vue\"\r\nimport type { EditorCommand } from \"./command\"\r\n\r\nconst t = useTranslations()\r\n\r\n/**\r\n * p5: one toolbar button for any EditorCommand, e.g.\r\n * `<CommandButton :command=\"markCommand('bold')\" />`. Replaces the template's\r\n * MarkButton, HeadingButton, ListButton, … (see UPSTREAM.md).\r\n */\r\nconst props = defineProps<{\r\n command: EditorCommand\r\n /** Text next to the icon, e.g. in dropdown menus */\r\n text?: string\r\n editor?: Editor | null\r\n}>()\r\n\r\nconst editor = useTiptapEditor(() => props.editor)\r\n\r\nconst { isActive, canRun } = toStateRefs(\r\n useEditorState(\r\n editor,\r\n (editor) => ({\r\n isActive: props.command.isActive?.(editor) ?? false,\r\n canRun: props.command.canRun(editor),\r\n }),\r\n { deps: () => props.command }\r\n )\r\n)\r\n</script>\r\n","import { computed, nextTick, ref, watch, type Ref } from \"vue\"\r\nimport { afterPointerRelease, isPointerDown } from \"@/lib/pointer\"\r\n\r\nexport type PopoverAlign = \"start\" | \"center\" | \"end\"\r\n\r\nexport interface UsePopoverOptions {\r\n open: () => boolean\r\n setOpen: (open: boolean) => void\r\n align?: () => PopoverAlign\r\n sideOffset?: () => number\r\n}\r\n\r\nlet uid = 0\r\n\r\nconst supportsAnchorPositioning =\r\n typeof CSS !== \"undefined\" && CSS.supports(\"anchor-name: --a\")\r\n\r\n/**\r\n * Shared logic for Popover and DropdownMenu, built on the native `popover`\r\n * attribute and CSS anchor positioning instead of Radix.\r\n *\r\n * - The trigger gets `popovertarget`, so the browser handles toggling, light\r\n * dismiss (click outside) and Escape.\r\n * - `open` stays controllable: the `toggle` event syncs the browser's state\r\n * back, and changing `open` programmatically calls show/hidePopover.\r\n * - Browsers without anchor positioning get a one-off position on open.\r\n * - An `[autofocus]` element inside the content is focused once it is shown.\r\n */\r\nexport function usePopover({\r\n open,\r\n setOpen,\r\n align = () => \"center\",\r\n sideOffset = () => 4,\r\n}: UsePopoverOptions) {\r\n const id = `tiptap-popover-${++uid}`\r\n const anchorName = `--${id}`\r\n const contentRef: Ref<HTMLElement | null> = ref(null)\r\n\r\n const isShown = () => contentRef.value?.matches(\":popover-open\") ?? false\r\n\r\n const positionWithoutAnchor = () => {\r\n const content = contentRef.value\r\n const trigger = document.querySelector(`[popovertarget=\"${id}\"]`)\r\n if (supportsAnchorPositioning || !content || !trigger) return\r\n\r\n const rect = trigger.getBoundingClientRect()\r\n const width = content.offsetWidth\r\n const left =\r\n align() === \"start\"\r\n ? rect.left\r\n : align() === \"end\"\r\n ? rect.right - width\r\n : rect.left + rect.width / 2 - width / 2\r\n\r\n content.style.top = `${rect.bottom + sideOffset()}px`\r\n content.style.left = `${Math.max(4, Math.min(left, window.innerWidth - width - 4))}px`\r\n }\r\n\r\n const show = () => {\r\n if (open() && !isShown()) contentRef.value?.showPopover()\r\n }\r\n\r\n watch(\r\n open,\r\n (value) => {\r\n const content = contentRef.value\r\n if (!content || value === isShown()) return\r\n if (!value) return content.hidePopover()\r\n\r\n // Opened from code while the mouse is still down, e.g. the link popover\r\n // auto-opening because a click put the cursor in a link. Shown right away,\r\n // the pointerup that follows counts as a click outside and light-dismisses it.\r\n if (isPointerDown()) afterPointerRelease(show)\r\n else show()\r\n },\r\n { flush: \"post\" }\r\n )\r\n\r\n const onToggle = (event: Event) => {\r\n const nextOpen = (event as ToggleEvent).newState === \"open\"\r\n if (nextOpen !== open()) setOpen(nextOpen)\r\n if (nextOpen) {\r\n requestAnimationFrame(positionWithoutAnchor)\r\n // Content renders after the popover opened (see Popover.vue), so the\r\n // browser's own autofocus handling can't be relied on. Queued after\r\n // setOpen, so it runs after that render.\r\n nextTick(() =>\r\n contentRef.value?.querySelector<HTMLElement>(\"[autofocus]\")?.focus()\r\n )\r\n }\r\n }\r\n\r\n const triggerAttrs = computed(() => ({\r\n popovertarget: id,\r\n \"aria-controls\": id,\r\n // A string: Vue 2 would drop the attribute when `false`\r\n \"aria-expanded\": String(open()),\r\n \"data-state\": open() ? \"open\" : \"closed\",\r\n style: { anchorName },\r\n }))\r\n\r\n const contentAttrs = computed(() => ({\r\n id,\r\n popover: \"auto\",\r\n \"data-state\": open() ? \"open\" : \"closed\",\r\n \"data-side\": \"bottom\",\r\n \"data-align\": align(),\r\n style: {\r\n positionAnchor: anchorName,\r\n \"--tt-popover-offset\": `${sideOffset()}px`,\r\n },\r\n }))\r\n\r\n const close = () => setOpen(false)\r\n\r\n // `onToggle` goes on the content with @toggle (a listener in v-bind only works in Vue 3)\r\n return { id, contentRef, triggerAttrs, contentAttrs, onToggle, close }\r\n}\r\n","<template>\n <div class=\"rt-portal\">\n <slot />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { getCurrentInstance, onBeforeUnmount, onMounted } from \"vue\"\n\n/**\n * Vue 2 version of the shared Portal.vue (Vue 2 has no Teleport): the element\n * is moved to the end of <body> once mounted, and Vue keeps patching it there.\n */\nconst instance = getCurrentInstance()\nconst element = () => instance?.proxy.$el\n\nonMounted(() => {\n const el = element()\n if (el instanceof HTMLElement) document.body.appendChild(el)\n})\n\n// When a parent is removed, Vue 2 only removes the parent's element, which no\n// longer contains this one\nonBeforeUnmount(() => {\n element()?.remove()\n})\n</script>\n","<template>\r\n <!-- One root element for Vue 2; display: contents keeps it out of the layout -->\r\n <span class=\"tiptap-popover-root\">\r\n <slot name=\"trigger\" :attrs=\"{ ...triggerAttrs, 'aria-haspopup': 'menu' }\" />\r\n <Portal>\r\n <div\r\n ref=\"contentRef\"\r\n v-bind=\"{ ...contentAttrs, ...$attrs }\"\r\n role=\"menu\"\r\n class=\"tiptap-dropdown-menu-content tiptap-anchored\"\r\n @toggle=\"onToggle\"\r\n @keydown=\"onKeyDown\"\r\n @click=\"onClick\"\r\n >\r\n <slot v-if=\"open\" />\r\n </div>\r\n </Portal>\r\n </span>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\n// Not defineOptions(): that needs Vue 3.3+, this also works in Vue 2.7\r\nexport default { inheritAttrs: false }\r\n</script>\r\n\r\n<script setup lang=\"ts\">\r\nimport { nextTick, watch } from \"vue\"\r\nimport { usePopover, type PopoverAlign } from \"@/composables/use-popover\"\r\nimport Portal from \"@/components/tiptap-ui-primitive/portal/Portal.vue\"\r\nimport \"@/components/tiptap-ui-primitive/dropdown-menu/dropdown-menu.scss\"\r\nimport \"@/components/tiptap-ui-primitive/popover/popover-anchor.scss\"\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n open?: boolean\r\n align?: PopoverAlign\r\n sideOffset?: number\r\n }>(),\r\n { open: false, align: \"start\", sideOffset: 4 }\r\n)\r\n\r\nconst emit = defineEmits<{ (e: \"update:open\", value: boolean): void }>()\r\n\r\nconst { contentRef, triggerAttrs, contentAttrs, onToggle, close } = usePopover({\r\n open: () => props.open,\r\n setOpen: (value) => emit(\"update:open\", value),\r\n align: () => props.align,\r\n sideOffset: () => props.sideOffset,\r\n})\r\n\r\nconst getItems = () =>\r\n Array.from(\r\n contentRef.value?.querySelectorAll<HTMLElement>(\r\n '[role=\"menuitem\"]:not([disabled]), button:not([disabled])'\r\n ) ?? []\r\n )\r\n\r\n// Like Radix: focus the first item when the menu opens\r\nwatch(\r\n () => props.open,\r\n async (open) => {\r\n if (!open) return\r\n await nextTick()\r\n getItems()[0]?.focus()\r\n }\r\n)\r\n\r\nfunction onKeyDown(event: KeyboardEvent) {\r\n const items = getItems()\r\n if (!items.length) return\r\n\r\n const current = items.indexOf(document.activeElement as HTMLElement)\r\n const focusAt = (index: number) => {\r\n event.preventDefault()\r\n items[(index + items.length) % items.length]?.focus()\r\n }\r\n\r\n switch (event.key) {\r\n case \"ArrowDown\":\r\n return focusAt(current + 1)\r\n case \"ArrowUp\":\r\n return focusAt(current - 1)\r\n case \"Home\":\r\n return focusAt(0)\r\n case \"End\":\r\n return focusAt(items.length - 1)\r\n }\r\n}\r\n\r\n// Selecting an item closes the menu, like Radix's DropdownMenuItem\r\nfunction onClick(event: MouseEvent) {\r\n const item = (event.target as HTMLElement).closest(\"button\")\r\n if (item && contentRef.value?.contains(item)) close()\r\n}\r\n</script>\r\n","<template>\r\n <DropdownMenu :open=\"isOpen\" @update:open=\"isOpen = $event\" align=\"start\">\r\n <template #trigger=\"{ attrs }\">\r\n <Button\r\n v-bind=\"attrs\"\r\n type=\"button\"\r\n :data-active-state=\"isActive ? 'on' : 'off'\"\r\n role=\"button\"\r\n :tabindex=\"-1\"\r\n :disabled=\"!canToggleHeading\"\r\n :data-disabled=\"!canToggleHeading\"\r\n :aria-label=\"t.heading.menu\"\r\n :aria-pressed=\"String(isActive)\"\r\n :tooltip=\"t.heading.button\"\r\n >\r\n <Icon\r\n :name=\"activeLevel ? headingIcons[activeLevel] : HeadingIcon\"\r\n class=\"tiptap-button-icon\"\r\n />\r\n <Icon :name=\"ChevronDownIcon\" class=\"tiptap-button-dropdown-small\" />\r\n </Button>\r\n </template>\r\n\r\n <div class=\"tiptap-dropdown-menu-group\">\r\n <CommandButton\r\n v-for=\"level in levels\"\r\n :key=\"level\"\r\n :command=\"headingCommand(level)\"\r\n :text=\"t.heading.level(level)\"\r\n role=\"menuitem\"\r\n :show-tooltip=\"false\"\r\n />\r\n </div>\r\n </DropdownMenu>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { useTranslations } from \"@/composables/use-translations\"\r\nimport { ref } from \"vue\"\r\nimport { useTiptapEditor } from \"@/composables/use-tiptap-editor\"\r\nimport { useEditorState, toStateRefs } from \"@/composables/use-editor-state\"\r\nimport { getActiveHeadingLevel } from \"@/components/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu\"\r\nimport {\r\n canToggle,\r\n headingCommand,\r\n headingIcons,\r\n isHeadingActive,\r\n type Level,\r\n} from \"@/components/tiptap-ui/heading-button\"\r\nimport CommandButton from \"@/components/tiptap-ui/CommandButton.vue\"\r\nimport { Button } from \"@/components/tiptap-ui-primitive/button\"\r\nimport { DropdownMenu } from \"@/components/tiptap-ui-primitive/dropdown-menu\"\r\nimport Icon from \"@/components/tiptap-icons/Icon.vue\"\r\nimport { ChevronDownIcon, HeadingIcon } from \"@/components/tiptap-icons\"\r\n\r\nconst t = useTranslations()\r\n\r\n/** Dropdown menu for choosing a heading level; the trigger shows the active one */\r\nconst props = withDefaults(defineProps<{ levels?: Level[] }>(), {\r\n levels: () => [1, 2, 3, 4, 5, 6],\r\n})\r\n\r\nconst editor = useTiptapEditor()\r\nconst isOpen = ref(false)\r\n\r\nconst {\r\n activeLevel,\r\n isActive,\r\n canToggle: canToggleHeading,\r\n} = toStateRefs(\r\n useEditorState(\r\n editor,\r\n (editor) => ({\r\n activeLevel: getActiveHeadingLevel(editor, props.levels),\r\n isActive: isHeadingActive(editor),\r\n canToggle: canToggle(editor),\r\n }),\r\n { deps: () => props.levels.join() }\r\n )\r\n)\r\n</script>\r\n","import { defineCommand } from \"@/components/tiptap-ui/command\"\r\nimport { type Editor } from \"@tiptap/core\"\r\nimport { NodeSelection, TextSelection } from \"@tiptap/pm/state\"\r\n\r\n// --- Icons ---\r\nimport { ListIcon, ListOrderedIcon, ListTodoIcon } from \"@/components/tiptap-icons\"\r\n\r\n// --- Lib ---\r\nimport {\r\n findNodePosition,\r\n getSelectedBlockNodes,\r\n isNodeInSchema,\r\n isNodeTypeSelected,\r\n isValidPosition,\r\n selectionWithinConvertibleTypes,\r\n} from \"@/lib/tiptap-utils\"\r\n\r\nexport type ListType = \"bulletList\" | \"orderedList\" | \"taskList\"\r\n\r\nexport const listIcons = {\r\n bulletList: ListIcon,\r\n orderedList: ListOrderedIcon,\r\n taskList: ListTodoIcon,\r\n}\r\n\r\nexport const LIST_SHORTCUT_KEYS: Record<ListType, string> = {\r\n bulletList: \"mod+shift+8\",\r\n orderedList: \"mod+shift+7\",\r\n taskList: \"mod+shift+9\",\r\n}\r\n\r\n/**\r\n * Checks if a list can be toggled in the current editor state\r\n */\r\nexport function canToggleList(\r\n editor: Editor | null,\r\n type: ListType,\r\n turnInto: boolean = true\r\n): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n if (!isNodeInSchema(type, editor) || isNodeTypeSelected(editor, [\"image\"]))\r\n return false\r\n\r\n if (!turnInto) {\r\n switch (type) {\r\n case \"bulletList\":\r\n return editor.can().toggleBulletList()\r\n case \"orderedList\":\r\n return editor.can().toggleOrderedList()\r\n case \"taskList\":\r\n return editor.can().toggleList(\"taskList\", \"taskItem\")\r\n default:\r\n return false\r\n }\r\n }\r\n\r\n // Ensure selection is in nodes we're allowed to convert\r\n if (\r\n !selectionWithinConvertibleTypes(editor, [\r\n \"paragraph\",\r\n \"heading\",\r\n \"bulletList\",\r\n \"orderedList\",\r\n \"taskList\",\r\n \"blockquote\",\r\n \"codeBlock\",\r\n ])\r\n )\r\n return false\r\n\r\n // Either we can set list directly on the selection,\r\n // or we can clear formatting/nodes to arrive at a list.\r\n switch (type) {\r\n case \"bulletList\":\r\n return editor.can().toggleBulletList() || editor.can().clearNodes()\r\n case \"orderedList\":\r\n return editor.can().toggleOrderedList() || editor.can().clearNodes()\r\n case \"taskList\":\r\n return (\r\n editor.can().toggleList(\"taskList\", \"taskItem\") ||\r\n editor.can().clearNodes()\r\n )\r\n default:\r\n return false\r\n }\r\n}\r\n\r\n/**\r\n * Checks if list is currently active\r\n */\r\nexport function isListActive(editor: Editor | null, type: ListType): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n\r\n switch (type) {\r\n case \"bulletList\":\r\n return editor.isActive(\"bulletList\")\r\n case \"orderedList\":\r\n return editor.isActive(\"orderedList\")\r\n case \"taskList\":\r\n return editor.isActive(\"taskList\")\r\n default:\r\n return false\r\n }\r\n}\r\n\r\n/**\r\n * Toggles list in the editor\r\n */\r\nexport function toggleList(editor: Editor | null, type: ListType): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n if (!canToggleList(editor, type)) return false\r\n\r\n try {\r\n const view = editor.view\r\n let state = view.state\r\n let tr = state.tr\r\n\r\n const blocks = getSelectedBlockNodes(editor)\r\n\r\n // In case a selection contains multiple blocks, we only allow\r\n // toggling to node if there's exactly one block selected\r\n // we also dont block the canToggle since it will fall back to the bottom logic\r\n const isPossibleToTurnInto =\r\n selectionWithinConvertibleTypes(editor, [\r\n \"paragraph\",\r\n \"heading\",\r\n \"bulletList\",\r\n \"orderedList\",\r\n \"taskList\",\r\n \"blockquote\",\r\n \"codeBlock\",\r\n ]) && blocks.length === 1\r\n\r\n // No selection, find the the cursor position\r\n if (\r\n (state.selection.empty || state.selection instanceof TextSelection) &&\r\n isPossibleToTurnInto\r\n ) {\r\n const pos = findNodePosition({\r\n editor,\r\n node: state.selection.$anchor.node(1),\r\n })?.pos\r\n if (!isValidPosition(pos)) return false\r\n\r\n tr = tr.setSelection(NodeSelection.create(state.doc, pos))\r\n view.dispatch(tr)\r\n state = view.state\r\n }\r\n\r\n const selection = state.selection\r\n\r\n let chain = editor.chain().focus()\r\n\r\n // Handle NodeSelection\r\n if (selection instanceof NodeSelection) {\r\n const firstChild = selection.node.firstChild?.firstChild\r\n const lastChild = selection.node.lastChild?.lastChild\r\n\r\n const from = firstChild\r\n ? selection.from + firstChild.nodeSize\r\n : selection.from + 1\r\n\r\n const to = lastChild\r\n ? selection.to - lastChild.nodeSize\r\n : selection.to - 1\r\n\r\n const resolvedFrom = state.doc.resolve(from)\r\n const resolvedTo = state.doc.resolve(to)\r\n\r\n chain = chain\r\n .setTextSelection(TextSelection.between(resolvedFrom, resolvedTo))\r\n .clearNodes()\r\n }\r\n\r\n if (editor.isActive(type)) {\r\n // Unwrap list\r\n chain\r\n .liftListItem(\"listItem\")\r\n .lift(\"bulletList\")\r\n .lift(\"orderedList\")\r\n .lift(\"taskList\")\r\n .run()\r\n } else {\r\n // Wrap in specific list type\r\n const toggleMap: Record<ListType, () => typeof chain> = {\r\n bulletList: () => chain.toggleBulletList(),\r\n orderedList: () => chain.toggleOrderedList(),\r\n taskList: () => chain.toggleList(\"taskList\", \"taskItem\"),\r\n }\r\n\r\n const toggle = toggleMap[type]\r\n if (!toggle) return false\r\n\r\n toggle().run()\r\n }\r\n\r\n editor.chain().focus().selectTextblockEnd().run()\r\n\r\n return true\r\n } catch {\r\n return false\r\n }\r\n}\r\n\r\n\r\n/** p5: toolbar command, rendered by CommandButton (see ../command.ts) */\r\nexport const listCommand = (type: ListType) =>\r\n defineCommand(`list:${type}`, () => ({\r\n label: (t) => t.list[type],\r\n icon: listIcons[type],\r\n shortcutKeys: LIST_SHORTCUT_KEYS[type],\r\n isActive: (editor) => isListActive(editor, type),\r\n canRun: (editor) => canToggleList(editor, type),\r\n run: (editor) => toggleList(editor, type),\r\n }))\r\n","import type { Editor } from \"@tiptap/core\"\r\n\r\n// --- Icons ---\r\nimport { ListIcon, ListOrderedIcon, ListTodoIcon } from \"@/components/tiptap-icons\"\r\nimport type { IconName } from \"@/components/tiptap-icons\"\r\n\r\n// --- Tiptap UI ---\r\nimport {\r\n canToggleList,\r\n isListActive,\r\n type ListType,\r\n} from \"@/components/tiptap-ui/list-button\"\r\n\r\nexport interface ListOption {\r\n type: ListType\r\n icon: IconName\r\n}\r\n\r\nexport const listOptions: ListOption[] = [\r\n {\r\n type: \"bulletList\",\r\n icon: ListIcon,\r\n },\r\n {\r\n type: \"orderedList\",\r\n icon: ListOrderedIcon,\r\n },\r\n {\r\n type: \"taskList\",\r\n icon: ListTodoIcon,\r\n },\r\n]\r\n\r\nexport function canToggleAnyList(\r\n editor: Editor | null,\r\n listTypes: ListType[]\r\n): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n return listTypes.some((type) => canToggleList(editor, type))\r\n}\r\n\r\nexport function isAnyListActive(\r\n editor: Editor | null,\r\n listTypes: ListType[]\r\n): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n return listTypes.some((type) => isListActive(editor, type))\r\n}\r\n\r\nexport function getFilteredListOptions(\r\n availableTypes: ListType[]\r\n): typeof listOptions {\r\n return listOptions.filter(\r\n (option) => !option.type || availableTypes.includes(option.type)\r\n )\r\n}\r\n\r\n/**\r\n * Gets the currently active list type from the available types\r\n */\r\nexport function getActiveListType(\r\n editor: Editor | null,\r\n availableTypes: ListType[]\r\n): ListType | undefined {\r\n if (!editor || !editor.isEditable) return undefined\r\n return availableTypes.find((type) => isListActive(editor, type))\r\n}\r\n","<template>\r\n <DropdownMenu :open=\"isOpen\" @update:open=\"isOpen = $event\" align=\"start\">\r\n <template #trigger=\"{ attrs }\">\r\n <Button\r\n v-bind=\"attrs\"\r\n type=\"button\"\r\n :data-active-state=\"isActive ? 'on' : 'off'\"\r\n role=\"button\"\r\n :tabindex=\"-1\"\r\n :disabled=\"!canToggle\"\r\n :data-disabled=\"!canToggle\"\r\n :aria-label=\"t.list.menu\"\r\n :tooltip=\"t.list.button\"\r\n >\r\n <Icon :name=\"activeType ? listIcons[activeType] : ListIcon\" class=\"tiptap-button-icon\" />\r\n <Icon :name=\"ChevronDownIcon\" class=\"tiptap-button-dropdown-small\" />\r\n </Button>\r\n </template>\r\n\r\n <div class=\"tiptap-dropdown-menu-group\">\r\n <CommandButton\r\n v-for=\"option in getFilteredListOptions(types)\"\r\n :key=\"option.type\"\r\n :command=\"listCommand(option.type)\"\r\n :text=\"t.list[option.type]\"\r\n role=\"menuitem\"\r\n :show-tooltip=\"false\"\r\n />\r\n </div>\r\n </DropdownMenu>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { useTranslations } from \"@/composables/use-translations\"\r\nimport { ref } from \"vue\"\r\nimport { useTiptapEditor } from \"@/composables/use-tiptap-editor\"\r\nimport { useEditorState, toStateRefs } from \"@/composables/use-editor-state\"\r\nimport {\r\n canToggleAnyList,\r\n getActiveListType,\r\n getFilteredListOptions,\r\n isAnyListActive,\r\n} from \"@/components/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu\"\r\nimport { listCommand, listIcons, type ListType } from \"@/components/tiptap-ui/list-button\"\r\nimport CommandButton from \"@/components/tiptap-ui/CommandButton.vue\"\r\nimport { Button } from \"@/components/tiptap-ui-primitive/button\"\r\nimport { DropdownMenu } from \"@/components/tiptap-ui-primitive/dropdown-menu\"\r\nimport Icon from \"@/components/tiptap-icons/Icon.vue\"\r\nimport { ChevronDownIcon, ListIcon } from \"@/components/tiptap-icons\"\r\n\r\nconst t = useTranslations()\r\n\r\n/** Dropdown menu for choosing a list type; the trigger shows the active one */\r\nconst props = withDefaults(defineProps<{ types?: ListType[] }>(), {\r\n types: () => [\"bulletList\", \"orderedList\", \"taskList\"],\r\n})\r\n\r\nconst editor = useTiptapEditor()\r\nconst isOpen = ref(false)\r\n\r\nconst { activeType, isActive, canToggle } = toStateRefs(\r\n useEditorState(\r\n editor,\r\n (editor) => ({\r\n activeType: getActiveListType(editor, props.types),\r\n isActive: isAnyListActive(editor, props.types),\r\n canToggle: canToggleAnyList(editor, props.types),\r\n }),\r\n { deps: () => props.types.join() }\r\n )\r\n)\r\n</script>\r\n","import { defineCommand } from \"@/components/tiptap-ui/command\"\r\nimport type { Editor } from \"@tiptap/core\"\r\nimport { NodeSelection, TextSelection } from \"@tiptap/pm/state\"\r\n\r\n// --- Icons ---\r\nimport { BlockquoteIcon } from \"@/components/tiptap-icons\"\r\n\r\n// --- UI Utils ---\r\nimport {\r\n findNodePosition,\r\n getSelectedBlockNodes,\r\n isNodeInSchema,\r\n isNodeTypeSelected,\r\n isValidPosition,\r\n selectionWithinConvertibleTypes,\r\n} from \"@/lib/tiptap-utils\"\r\n\r\nexport const BLOCKQUOTE_SHORTCUT_KEY = \"mod+shift+b\"\r\n\r\n/**\r\n * Checks if blockquote can be toggled in the current editor state\r\n */\r\nexport function canToggleBlockquote(\r\n editor: Editor | null,\r\n turnInto: boolean = true\r\n): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n if (\r\n !isNodeInSchema(\"blockquote\", editor) ||\r\n isNodeTypeSelected(editor, [\"image\"])\r\n )\r\n return false\r\n\r\n if (!turnInto) {\r\n return editor.can().toggleWrap(\"blockquote\")\r\n }\r\n\r\n // Ensure selection is in nodes we're allowed to convert\r\n if (\r\n !selectionWithinConvertibleTypes(editor, [\r\n \"paragraph\",\r\n \"heading\",\r\n \"bulletList\",\r\n \"orderedList\",\r\n \"taskList\",\r\n \"blockquote\",\r\n \"codeBlock\",\r\n ])\r\n )\r\n return false\r\n\r\n // Either we can wrap in blockquote directly on the selection,\r\n // or we can clear formatting/nodes to arrive at a blockquote.\r\n return editor.can().toggleWrap(\"blockquote\") || editor.can().clearNodes()\r\n}\r\n\r\n/**\r\n * Toggles blockquote formatting for a specific node or the current selection\r\n */\r\nexport function toggleBlockquote(editor: Editor | null): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n if (!canToggleBlockquote(editor)) return false\r\n\r\n try {\r\n const view = editor.view\r\n let state = view.state\r\n let tr = state.tr\r\n\r\n const blocks = getSelectedBlockNodes(editor)\r\n\r\n // In case a selection contains multiple blocks, we only allow\r\n // toggling to node if there's exactly one block selected\r\n // we also dont block the canToggle since it will fall back to the bottom logic\r\n const isPossibleToTurnInto =\r\n selectionWithinConvertibleTypes(editor, [\r\n \"paragraph\",\r\n \"heading\",\r\n \"bulletList\",\r\n \"orderedList\",\r\n \"taskList\",\r\n \"blockquote\",\r\n \"codeBlock\",\r\n ]) && blocks.length === 1\r\n\r\n // No selection, find the the cursor position\r\n if (\r\n (state.selection.empty || state.selection instanceof TextSelection) &&\r\n isPossibleToTurnInto\r\n ) {\r\n const pos = findNodePosition({\r\n editor,\r\n node: state.selection.$anchor.node(1),\r\n })?.pos\r\n if (!isValidPosition(pos)) return false\r\n\r\n tr = tr.setSelection(NodeSelection.create(state.doc, pos))\r\n view.dispatch(tr)\r\n state = view.state\r\n }\r\n\r\n const selection = state.selection\r\n\r\n let chain = editor.chain().focus()\r\n\r\n // Handle NodeSelection\r\n if (selection instanceof NodeSelection) {\r\n const firstChild = selection.node.firstChild?.firstChild\r\n const lastChild = selection.node.lastChild?.lastChild\r\n\r\n const from = firstChild\r\n ? selection.from + firstChild.nodeSize\r\n : selection.from + 1\r\n\r\n const to = lastChild\r\n ? selection.to - lastChild.nodeSize\r\n : selection.to - 1\r\n\r\n const resolvedFrom = state.doc.resolve(from)\r\n const resolvedTo = state.doc.resolve(to)\r\n\r\n chain = chain\r\n .setTextSelection(TextSelection.between(resolvedFrom, resolvedTo))\r\n .clearNodes()\r\n }\r\n\r\n const toggle = editor.isActive(\"blockquote\")\r\n ? chain.lift(\"blockquote\")\r\n : chain.wrapIn(\"blockquote\")\r\n\r\n toggle.run()\r\n\r\n editor.chain().focus().selectTextblockEnd().run()\r\n\r\n return true\r\n } catch {\r\n return false\r\n }\r\n}\r\n\r\n\r\n/** p5: toolbar command, rendered by CommandButton (see ../command.ts) */\r\nexport const blockquoteCommand = () =>\r\n defineCommand(\"blockquote\", () => ({\r\n label: (t) => t.blockquote,\r\n icon: BlockquoteIcon,\r\n shortcutKeys: BLOCKQUOTE_SHORTCUT_KEY,\r\n isActive: (editor) => editor?.isActive(\"blockquote\") ?? false,\r\n canRun: (editor) => canToggleBlockquote(editor),\r\n run: (editor) => toggleBlockquote(editor),\r\n }))\r\n","import { defineCommand } from \"@/components/tiptap-ui/command\"\r\nimport { type Editor } from \"@tiptap/core\"\r\nimport { NodeSelection, TextSelection } from \"@tiptap/pm/state\"\r\n\r\n// --- Lib ---\r\nimport {\r\n findNodePosition,\r\n getSelectedBlockNodes,\r\n isNodeInSchema,\r\n isNodeTypeSelected,\r\n isValidPosition,\r\n selectionWithinConvertibleTypes,\r\n} from \"@/lib/tiptap-utils\"\r\n\r\n// --- Icons ---\r\nimport { CodeBlockIcon } from \"@/components/tiptap-icons\"\r\n\r\nexport const CODE_BLOCK_SHORTCUT_KEY = \"mod+alt+c\"\r\n\r\n/**\r\n * Checks if code block can be toggled in the current editor state\r\n */\r\nexport function canToggle(\r\n editor: Editor | null,\r\n turnInto: boolean = true\r\n): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n if (\r\n !isNodeInSchema(\"codeBlock\", editor) ||\r\n isNodeTypeSelected(editor, [\"image\"])\r\n )\r\n return false\r\n\r\n if (!turnInto) {\r\n return editor.can().toggleNode(\"codeBlock\", \"paragraph\")\r\n }\r\n\r\n // Ensure selection is in nodes we're allowed to convert\r\n if (\r\n !selectionWithinConvertibleTypes(editor, [\r\n \"paragraph\",\r\n \"heading\",\r\n \"bulletList\",\r\n \"orderedList\",\r\n \"taskList\",\r\n \"blockquote\",\r\n \"codeBlock\",\r\n ])\r\n )\r\n return false\r\n\r\n // Either we can toggle code block directly on the selection,\r\n // or we can clear formatting/nodes to arrive at a code block.\r\n return (\r\n editor.can().toggleNode(\"codeBlock\", \"paragraph\") ||\r\n editor.can().clearNodes()\r\n )\r\n}\r\n\r\n/**\r\n * Toggles code block in the editor\r\n */\r\nexport function toggleCodeBlock(editor: Editor | null): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n if (!canToggle(editor)) return false\r\n\r\n try {\r\n const view = editor.view\r\n let state = view.state\r\n let tr = state.tr\r\n\r\n const blocks = getSelectedBlockNodes(editor)\r\n\r\n // In case a selection contains multiple blocks, we only allow\r\n // toggling to node if there's exactly one block selected\r\n // we also dont block the canToggle since it will fall back to the bottom logic\r\n const isPossibleToTurnInto =\r\n selectionWithinConvertibleTypes(editor, [\r\n \"paragraph\",\r\n \"heading\",\r\n \"bulletList\",\r\n \"orderedList\",\r\n \"taskList\",\r\n \"blockquote\",\r\n \"codeBlock\",\r\n ]) && blocks.length === 1\r\n\r\n // No selection, find the the cursor position\r\n if (\r\n (state.selection.empty || state.selection instanceof TextSelection) &&\r\n isPossibleToTurnInto\r\n ) {\r\n const pos = findNodePosition({\r\n editor,\r\n node: state.selection.$anchor.node(1),\r\n })?.pos\r\n if (!isValidPosition(pos)) return false\r\n\r\n tr = tr.setSelection(NodeSelection.create(state.doc, pos))\r\n view.dispatch(tr)\r\n state = view.state\r\n }\r\n\r\n const selection = state.selection\r\n\r\n let chain = editor.chain().focus()\r\n\r\n // Handle NodeSelection\r\n if (selection instanceof NodeSelection) {\r\n const firstChild = selection.node.firstChild?.firstChild\r\n const lastChild = selection.node.lastChild?.lastChild\r\n\r\n const from = firstChild\r\n ? selection.from + firstChild.nodeSize\r\n : selection.from + 1\r\n\r\n const to = lastChild\r\n ? selection.to - lastChild.nodeSize\r\n : selection.to - 1\r\n\r\n const resolvedFrom = state.doc.resolve(from)\r\n const resolvedTo = state.doc.resolve(to)\r\n\r\n chain = chain\r\n .setTextSelection(TextSelection.between(resolvedFrom, resolvedTo))\r\n .clearNodes()\r\n }\r\n\r\n const toggle = editor.isActive(\"codeBlock\")\r\n ? chain.setNode(\"paragraph\")\r\n : chain.toggleNode(\"codeBlock\", \"paragraph\")\r\n\r\n toggle.run()\r\n\r\n editor.chain().focus().selectTextblockEnd().run()\r\n\r\n return true\r\n } catch {\r\n return false\r\n }\r\n}\r\n\r\n\r\n/** p5: toolbar command, rendered by CommandButton (see ../command.ts) */\r\nexport const codeBlockCommand = () =>\r\n defineCommand(\"codeBlock\", () => ({\r\n label: (t) => t.codeBlock,\r\n icon: CodeBlockIcon,\r\n shortcutKeys: CODE_BLOCK_SHORTCUT_KEY,\r\n isActive: (editor) => editor?.isActive(\"codeBlock\") ?? false,\r\n canRun: (editor) => canToggle(editor),\r\n run: (editor) => toggleCodeBlock(editor),\r\n }))\r\n","import type { Editor } from \"@tiptap/core\"\nimport { defineCommand } from \"@/components/tiptap-ui/command\"\n\n// --- Lib ---\nimport { isMarkInSchema, isNodeTypeSelected } from \"@/lib/tiptap-utils\"\n\n// --- Icons ---\nimport { BanIcon, HighlighterIcon } from \"@/components/tiptap-icons\"\n\n// p5: opens the highlight popover (see InputRichtext's shortcuts)\nexport const COLOR_HIGHLIGHT_SHORTCUT_KEY = \"mod+shift+h\"\nexport const HIGHLIGHT_COLORS = [\n {\n name: \"default\",\n value: \"var(--tt-bg-color)\",\n colorValue: \"#ffffff\",\n border: \"var(--tt-bg-color-contrast)\",\n },\n {\n name: \"gray\",\n value: \"var(--tt-color-highlight-gray)\",\n colorValue: \"#f8f8f7\",\n border: \"var(--tt-color-highlight-gray-contrast)\",\n },\n {\n name: \"brown\",\n value: \"var(--tt-color-highlight-brown)\",\n colorValue: \"#f4eeee\",\n border: \"var(--tt-color-highlight-brown-contrast)\",\n },\n {\n name: \"orange\",\n value: \"var(--tt-color-highlight-orange)\",\n colorValue: \"#fbecdd\",\n border: \"var(--tt-color-highlight-orange-contrast)\",\n },\n {\n name: \"yellow\",\n value: \"var(--tt-color-highlight-yellow)\",\n colorValue: \"#fef9c3\",\n border: \"var(--tt-color-highlight-yellow-contrast)\",\n },\n {\n name: \"green\",\n value: \"var(--tt-color-highlight-green)\",\n colorValue: \"#dcfce7\",\n border: \"var(--tt-color-highlight-green-contrast)\",\n },\n {\n name: \"blue\",\n value: \"var(--tt-color-highlight-blue)\",\n colorValue: \"#e0f2fe\",\n border: \"var(--tt-color-highlight-blue-contrast)\",\n },\n {\n name: \"purple\",\n value: \"var(--tt-color-highlight-purple)\",\n colorValue: \"#f3e8ff\",\n border: \"var(--tt-color-highlight-purple-contrast)\",\n },\n {\n name: \"pink\",\n value: \"var(--tt-color-highlight-pink)\",\n colorValue: \"#fcf1f6\",\n border: \"var(--tt-color-highlight-pink-contrast)\",\n },\n {\n name: \"red\",\n value: \"var(--tt-color-highlight-red)\",\n colorValue: \"#ffe4e6\",\n border: \"var(--tt-color-highlight-red-contrast)\",\n },\n] as const\nexport type HighlightColor = (typeof HIGHLIGHT_COLORS)[number]\n\nexport function pickHighlightColorsByValue(values: string[]) {\n const colorMap = new Map<string, HighlightColor>(\n HIGHLIGHT_COLORS.map((color) => [color.value, color])\n )\n return values\n .map((value) => colorMap.get(value))\n .filter((color): color is (typeof HIGHLIGHT_COLORS)[number] => !!color)\n}\n\n/**\n * Checks if a highlight can be applied in the current editor state\n * p5: only the highlight mark; the template's \"node background\" mode is removed\n */\nexport function canColorHighlight(editor: Editor | null): boolean {\n if (!editor || !editor.isEditable) return false\n if (!isMarkInSchema(\"highlight\", editor) || isNodeTypeSelected(editor, [\"image\"]))\n return false\n\n return editor.can().setMark(\"highlight\")\n}\n\n/**\n * Checks if a highlight (optionally of a specific color) is active\n */\nexport function isColorHighlightActive(\n editor: Editor | null,\n highlightColor?: string\n): boolean {\n if (!editor || !editor.isEditable) return false\n return highlightColor\n ? editor.isActive(\"highlight\", { color: highlightColor })\n : editor.isActive(\"highlight\")\n}\n\n/**\n * Applies a highlight color, or removes it when the selection already has it\n */\nexport function toggleColorHighlight(editor: Editor | null, color: string): boolean {\n if (!editor || !canColorHighlight(editor)) return false\n\n // A stored highlight mark (from typing after a highlight) would otherwise win\n const highlightMarkType = editor.schema.marks.highlight\n if (editor.state.storedMarks && highlightMarkType) {\n editor.view.dispatch(editor.state.tr.removeStoredMark(highlightMarkType))\n }\n\n // As in the template: after the click that closes the popover is handled\n setTimeout(() => editor.chain().focus().toggleHighlight({ color }).run(), 0)\n return true\n}\n\n/**\n * Removes the highlight from the selection\n */\nexport function removeHighlight(editor: Editor | null): boolean {\n if (!editor || !canColorHighlight(editor)) return false\n return editor.chain().focus().unsetMark(\"highlight\").run()\n}\n\n/** p5: toolbar command per color, rendered by CommandButton (see ../command.ts) */\nexport const highlightCommand = (color: HighlightColor) =>\n defineCommand(`highlight:${color.value}`, () => ({\n label: (t) => t.highlight.colors[color.name],\n icon: HighlighterIcon,\n isActive: (editor) => isColorHighlightActive(editor, color.value),\n canRun: (editor) => canColorHighlight(editor),\n run: (editor) => toggleColorHighlight(editor, color.value),\n }))\n\nexport const removeHighlightCommand = () =>\n defineCommand(\"highlight:none\", () => ({\n label: (t) => t.highlight.remove,\n icon: BanIcon,\n canRun: (editor) => canColorHighlight(editor),\n run: (editor) => removeHighlight(editor),\n }))\n","<template>\r\n <!-- One root element for Vue 2; display: contents keeps it out of the layout -->\r\n <span class=\"tiptap-popover-root\">\r\n <slot name=\"trigger\" :attrs=\"triggerAttrs\" />\r\n <Portal>\r\n <div\r\n ref=\"contentRef\"\r\n v-bind=\"{ ...contentAttrs, ...$attrs }\"\r\n class=\"tiptap-popover tiptap-anchored\"\r\n @toggle=\"onToggle\"\r\n >\r\n <slot v-if=\"open\" />\r\n </div>\r\n </Portal>\r\n </span>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\n// Not defineOptions(): that needs Vue 3.3+, this also works in Vue 2.7\r\nexport default { inheritAttrs: false }\r\n</script>\r\n\r\n<script setup lang=\"ts\">\r\nimport { usePopover, type PopoverAlign } from \"@/composables/use-popover\"\r\nimport Portal from \"@/components/tiptap-ui-primitive/portal/Portal.vue\"\r\nimport \"@/components/tiptap-ui-primitive/popover/popover.scss\"\r\nimport \"@/components/tiptap-ui-primitive/popover/popover-anchor.scss\"\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n open?: boolean\r\n align?: PopoverAlign\r\n sideOffset?: number\r\n }>(),\r\n { open: false, align: \"center\", sideOffset: 4 }\r\n)\r\n\r\nconst emit = defineEmits<{ (e: \"update:open\", value: boolean): void }>()\r\n\r\n/**\r\n * Bind `attrs` from the trigger slot onto a <button>. Content is only rendered\r\n * while open, and teleported to <body> so it isn't part of the toolbar's\r\n * keyboard navigation (Radix portals it for the same reason).\r\n */\r\nconst { contentRef, triggerAttrs, contentAttrs, onToggle } = usePopover({\r\n open: () => props.open,\r\n setOpen: (value) => emit(\"update:open\", value),\r\n align: () => props.align,\r\n sideOffset: () => props.sideOffset,\r\n})\r\n</script>\r\n","<template>\r\n <Button\r\n type=\"button\"\r\n role=\"button\"\r\n :tabindex=\"-1\"\r\n @click=\"emit('click', $event)\"\r\n :aria-label=\"t.highlight.button\"\r\n :tooltip=\"t.highlight.button\"\r\n >\r\n <slot>\r\n <Icon :name=\"HighlighterIcon\" class=\"tiptap-button-icon\" />\r\n </slot>\r\n </Button>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { useTranslations } from \"@/composables/use-translations\"\r\nimport { Button } from \"@/components/tiptap-ui-primitive/button\"\r\nimport Icon from \"@/components/tiptap-icons/Icon.vue\"\r\nimport { HighlighterIcon } from \"@/components/tiptap-icons\"\r\n\r\nconst t = useTranslations()\r\nconst emit = defineEmits<{ (e: \"click\", event: MouseEvent): void }>()\r\n</script>\r\n","import { onBeforeUnmount, onMounted, ref, type Ref } from \"vue\"\n\ntype BreakpointMode = \"min\" | \"max\"\n\n/**\n * Detects whether the current viewport matches a given breakpoint rule.\n * Example:\n * useIsBreakpoint(\"max\", 768) // true when width < 768\n * useIsBreakpoint(\"min\", 1024) // true when width >= 1024\n */\nexport function useIsBreakpoint(\n mode: BreakpointMode = \"max\",\n breakpoint = 768\n): Readonly<Ref<boolean>> {\n const matches = ref(false)\n let mql: MediaQueryList | null = null\n const onChange = (e: MediaQueryListEvent) => (matches.value = e.matches)\n\n onMounted(() => {\n const query =\n mode === \"min\"\n ? `(min-width: ${breakpoint}px)`\n : `(max-width: ${breakpoint - 1}px)`\n\n mql = window.matchMedia(query)\n matches.value = mql.matches\n mql.addEventListener(\"change\", onChange)\n })\n\n onBeforeUnmount(() => mql?.removeEventListener(\"change\", onChange))\n\n return matches\n}\n","<template>\r\n <Card\r\n ref=\"cardRef\"\r\n :tabindex=\"0\"\r\n :style=\"isMobile ? { boxShadow: 'none', border: 0 } : {}\"\r\n >\r\n <CardBody :style=\"isMobile ? { padding: 0 } : {}\">\r\n <CardItemGroup orientation=\"horizontal\">\r\n <ButtonGroup>\r\n <ButtonGroup v-for=\"(color, index) in colors\" :key=\"color.value\">\r\n <CommandButton\r\n :command=\"highlightCommand(color)\"\r\n :tabindex=\"index === selectedIndex ? 0 : -1\"\r\n :data-highlighted=\"selectedIndex === index\"\r\n :style=\"{ '--highlight-color': color.value }\"\r\n >\r\n <span class=\"tiptap-button-highlight\" />\r\n </CommandButton>\r\n </ButtonGroup>\r\n </ButtonGroup>\r\n <Separator />\r\n <ButtonGroup>\r\n <CommandButton\r\n :command=\"removeHighlightCommand()\"\r\n role=\"menuitem\"\r\n :tabindex=\"selectedIndex === colors.length ? 0 : -1\"\r\n :data-highlighted=\"selectedIndex === colors.length\"\r\n />\r\n </ButtonGroup>\r\n </CardItemGroup>\r\n </CardBody>\r\n </Card>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, ref } from \"vue\"\r\nimport { useIsBreakpoint } from \"@/composables/use-is-breakpoint\"\r\nimport { useMenuNavigation } from \"@/composables/use-menu-navigation\"\r\nimport {\r\n highlightCommand,\r\n pickHighlightColorsByValue,\r\n removeHighlightCommand,\r\n type HighlightColor,\r\n} from \"@/components/tiptap-ui/color-highlight-button\"\r\nimport CommandButton from \"@/components/tiptap-ui/CommandButton.vue\"\r\nimport { ButtonGroup } from \"@/components/tiptap-ui-primitive/button-group\"\r\nimport { Card, CardBody, CardItemGroup } from \"@/components/tiptap-ui-primitive/card\"\r\nimport { Separator } from \"@/components/tiptap-ui-primitive/separator\"\r\nimport \"@/components/tiptap-ui/color-highlight-button/color-highlight-button.scss\"\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n /** Colors to offer. Defaults to a predefined set. */\r\n colors?: HighlightColor[]\r\n }>(),\r\n {\r\n colors: () =>\r\n pickHighlightColorsByValue([\r\n \"var(--tt-color-highlight-green)\",\r\n \"var(--tt-color-highlight-blue)\",\r\n \"var(--tt-color-highlight-red)\",\r\n \"var(--tt-color-highlight-purple)\",\r\n \"var(--tt-color-highlight-yellow)\",\r\n ]),\r\n }\r\n)\r\n\r\nconst isMobile = useIsBreakpoint()\r\nconst cardRef = ref<{ $el: HTMLElement } | null>(null)\r\nconst containerRef = computed(() => cardRef.value?.$el ?? null)\r\n\r\n// Arrow keys move between the colors and the remove button; Enter picks one\r\nconst { selectedIndex } = useMenuNavigation({\r\n containerRef,\r\n items: () => [...props.colors, null],\r\n orientation: \"both\",\r\n onSelect: () =>\r\n containerRef.value?.querySelector<HTMLElement>('[data-highlighted=\"true\"]')?.click(),\r\n autoSelectFirstItem: false,\r\n})\r\n</script>\r\n","<template>\r\n <Popover :open=\"isOpen\" @update:open=\"isOpen = $event\" :aria-label=\"t.highlight.menu\">\r\n <template #trigger=\"{ attrs }\">\r\n <ColorHighlightPopoverButton\r\n v-bind=\"attrs\"\r\n :disabled=\"!canHighlight\"\r\n :data-active-state=\"isActive ? 'on' : 'off'\"\r\n :data-disabled=\"!canHighlight\"\r\n :aria-pressed=\"String(isActive)\"\r\n :shortcut-keys=\"COLOR_HIGHLIGHT_SHORTCUT_KEY\"\r\n :data-rt-shortcut=\"COLOR_HIGHLIGHT_SHORTCUT_KEY\"\r\n />\r\n </template>\r\n\r\n <ColorHighlightPopoverContent />\r\n </Popover>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { useTranslations } from \"@/composables/use-translations\"\r\nimport { ref } from \"vue\"\r\nimport { useTiptapEditor } from \"@/composables/use-tiptap-editor\"\r\nimport { useEditorState, toStateRefs } from \"@/composables/use-editor-state\"\r\nimport {\r\n canColorHighlight,\r\n COLOR_HIGHLIGHT_SHORTCUT_KEY,\r\n isColorHighlightActive,\r\n} from \"@/components/tiptap-ui/color-highlight-button\"\r\nimport { Popover } from \"@/components/tiptap-ui-primitive/popover\"\r\nimport ColorHighlightPopoverButton from \"./ColorHighlightPopoverButton.vue\"\r\nimport ColorHighlightPopoverContent from \"./ColorHighlightPopoverContent.vue\"\r\n\r\nconst t = useTranslations()\r\n\r\n/**\r\n * Highlight button that opens a popover with color choices.\r\n * `data-rt-shortcut` lets InputRichtext open it with Ctrl+Shift+H.\r\n */\r\nconst editor = useTiptapEditor()\r\nconst isOpen = ref(false)\r\n\r\nconst { isActive, canHighlight } = toStateRefs(\r\n useEditorState(editor, (editor) => ({\r\n isActive: isColorHighlightActive(editor),\r\n canHighlight: canColorHighlight(editor),\r\n }))\r\n)\r\n</script>\r\n","import { isMac } from \"@/lib/tiptap-utils\"\n\n/**\n * p5: keyboard shortcuts handled by InputRichtext, on top of Tiptap's own\n * (bold, lists, headings, …). Buttons that open a popover or menu carry\n * `data-rt-shortcut`, and InputRichtext clicks them on a matching key press.\n */\nexport const SHORTCUTS = {\n focusToolbar: \"alt+f10\",\n clearFormatting: \"mod+\\\\\",\n link: \"mod+k\",\n highlight: \"mod+shift+h\",\n image: \"mod+alt+i\",\n file: \"mod+alt+f\",\n table: \"mod+alt+t\",\n visibilityCondition: \"mod+alt+v\",\n} as const\n\n/**\n * Whether a keydown matches e.g. \"mod+shift+h\" (\"mod\" is Ctrl, or ⌘ on Mac).\n *\n * Compares `event.key`, so AltGr characters don't trigger Ctrl+Alt shortcuts:\n * on Windows AltGr is Ctrl+Alt, and on e.g. US-International AltGr+I types \"í\"\n * (event.key \"í\", not \"i\"). On Mac, Option changes event.key too (Option+I is a\n * dead key), so letters fall back to event.code there.\n */\nexport function matchesShortcut(event: KeyboardEvent, keys: string): boolean {\n const parts = keys.toLowerCase().split(\"+\")\n const key = parts.pop() ?? \"\"\n const wants = (modifier: string) => parts.includes(modifier)\n const mod = isMac() ? event.metaKey : event.ctrlKey\n\n if (\n wants(\"mod\") !== mod ||\n wants(\"shift\") !== event.shiftKey ||\n wants(\"alt\") !== event.altKey\n ) {\n return false\n }\n\n if (event.key.toLowerCase() === key) return true\n return (\n isMac() && event.altKey && /^[a-z]$/.test(key) && event.code === `Key${key.toUpperCase()}`\n )\n}\n","import { computed } from \"vue\"\r\nimport { useTiptapEditor } from \"@/composables/use-tiptap-editor\"\r\nimport { useEditorState, toStateRefs } from \"@/composables/use-editor-state\"\r\nimport { resolveValue, type MaybeGetters } from \"@/composables/utils\"\r\nimport { ref, watch, type Ref } from \"vue\"\r\nimport type { Editor } from \"@tiptap/core\"\r\n\r\n// --- Icons ---\r\nimport { LinkIcon } from \"@/components/tiptap-icons\"\r\n\r\n// --- Lib ---\r\nimport {\r\n isMarkInSchema,\r\n isNodeTypeSelected,\r\n sanitizeUrl,\r\n} from \"@/lib/tiptap-utils\"\r\n\r\n// p5: not in the React template\r\nexport const LINK_SHORTCUT_KEY = \"mod+k\"\r\n\r\n/**\r\n * Configuration for the link popover functionality\r\n */\r\nexport interface UseLinkPopoverConfig {\r\n /**\r\n * The Tiptap editor instance.\r\n */\r\n editor?: Editor | null\r\n /**\r\n * Whether to hide the link popover when not available.\r\n * @default false\r\n */\r\n hideWhenUnavailable?: boolean\r\n /**\r\n * Callback function called when the link is set.\r\n */\r\n onSetLink?: () => void\r\n}\r\n\r\n/**\r\n * Configuration for the link handler functionality\r\n */\r\nexport interface LinkHandlerProps {\r\n /**\r\n * The Tiptap editor instance.\r\n */\r\n editor: Editor | null\r\n /**\r\n * Callback function called when the link is set.\r\n */\r\n onSetLink?: () => void\r\n}\r\n\r\n/**\r\n * Checks if a link can be set in the current editor state\r\n */\r\nexport function canSetLink(editor: Editor | null): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n\r\n // The third argument 'true' checks whether the current selection is inside an image caption, and prevents setting a link there\r\n // If the selection is inside an image caption, we can't set a link\r\n if (isNodeTypeSelected(editor, [\"image\"], true)) return false\r\n try {\r\n return editor.can().setMark(\"link\")\r\n } catch {\r\n return false\r\n }\r\n}\r\n\r\n/**\r\n * Checks if a link is currently active in the editor\r\n */\r\nexport function isLinkActive(editor: Editor | null): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n return editor.isActive(\"link\")\r\n}\r\n\r\n/**\r\n * Determines if the link button should be shown\r\n */\r\nexport function shouldShowLinkButton(props: {\r\n editor: Editor | null\r\n hideWhenUnavailable: boolean\r\n}): boolean {\r\n const { editor, hideWhenUnavailable } = props\r\n\r\n if (!editor || !editor.isEditable) return false\r\n\r\n const linkInSchema = isMarkInSchema(\"link\", editor)\r\n\r\n // If hideWhenUnavailable is false, always show the button (even if disabled)\r\n if (!hideWhenUnavailable) {\r\n return true\r\n }\r\n\r\n // hideWhenUnavailable is true: hide if link is not in schema\r\n if (!linkInSchema) {\r\n return false\r\n }\r\n\r\n // hideWhenUnavailable is true: hide if we can't set a link (unless in code block)\r\n if (!editor.isActive(\"code\")) {\r\n return canSetLink(editor)\r\n }\r\n\r\n return true\r\n}\r\n\r\n// --- Vue composable ---\r\n/**\r\n * URL state and link actions. The URL follows the link under the cursor.\r\n */\r\nexport function useLinkHandler(props: {\r\n editor: Ref<Editor | null>\r\n onSetLink?: () => void\r\n}) {\r\n const { editor, onSetLink } = props\r\n const url = ref<string | null>(null)\r\n // p5: whether the link opens in a new tab; new links do by default\r\n const openInNewTab = ref(true)\r\n\r\n const syncUrl = () => {\r\n const { href, target } = editor.value?.getAttributes(\"link\") ?? {}\r\n url.value = href || \"\"\r\n openInNewTab.value = href ? target !== \"_self\" : true\r\n }\r\n\r\n watch(\r\n editor,\r\n (currentEditor, _previous, onCleanup) => {\r\n if (!currentEditor) return\r\n\r\n // Get URL immediately on mount\r\n if (isLinkActive(currentEditor) && url.value === null) syncUrl()\r\n\r\n currentEditor.on(\"selectionUpdate\", syncUrl)\r\n onCleanup(() => currentEditor.off(\"selectionUpdate\", syncUrl))\r\n },\r\n { immediate: true }\r\n )\r\n\r\n const setLink = () => {\r\n const currentEditor = editor.value\r\n if (!url.value || !currentEditor) return\r\n\r\n const isEmpty = currentEditor.state.selection.empty\r\n\r\n let chain = currentEditor\r\n .chain()\r\n .focus()\r\n .extendMarkRange(\"link\")\r\n .setLink({ href: url.value, target: openInNewTab.value ? \"_blank\" : \"_self\" })\r\n\r\n if (isEmpty) {\r\n chain = chain.insertContent({ type: \"text\", text: url.value })\r\n }\r\n\r\n chain.run()\r\n url.value = null\r\n onSetLink?.()\r\n }\r\n\r\n const removeLink = () => {\r\n editor.value\r\n ?.chain()\r\n .focus()\r\n .extendMarkRange(\"link\")\r\n .unsetLink()\r\n .setMeta(\"preventAutolink\", true)\r\n .run()\r\n url.value = \"\"\r\n }\r\n\r\n /** Updates an existing link right away; otherwise applies to the next setLink */\r\n const toggleNewTab = () => {\r\n openInNewTab.value = !openInNewTab.value\r\n const currentEditor = editor.value\r\n if (!currentEditor || !isLinkActive(currentEditor)) return\r\n\r\n currentEditor\r\n .chain()\r\n .extendMarkRange(\"link\")\r\n .updateAttributes(\"link\", { target: openInNewTab.value ? \"_blank\" : \"_self\" })\r\n .run()\r\n }\r\n\r\n const openLink = (\r\n target: string = \"_blank\",\r\n features: string = \"noopener,noreferrer\"\r\n ) => {\r\n if (!url.value) return\r\n\r\n const safeUrl = sanitizeUrl(url.value, window.location.href)\r\n if (safeUrl !== \"#\") {\r\n window.open(safeUrl, target, features)\r\n }\r\n }\r\n\r\n return {\r\n /** Writable, so it can be used with v-model */\r\n url: computed({\r\n get: () => url.value || \"\",\r\n set: (value: string) => (url.value = value),\r\n }),\r\n openInNewTab: computed(() => openInNewTab.value),\r\n toggleNewTab,\r\n setLink,\r\n removeLink,\r\n openLink,\r\n }\r\n}\r\n\r\n/**\r\n * Link button state, plus whether the popover should open by itself because\r\n * the cursor moved into a link while the editor has focus.\r\n */\r\nexport function useLinkState(props: {\r\n editor: Ref<Editor | null>\r\n hideWhenUnavailable: () => boolean\r\n}) {\r\n const state = useEditorState(\r\n props.editor,\r\n (editor) => ({\r\n isVisible: shouldShowLinkButton({ editor, hideWhenUnavailable: props.hideWhenUnavailable() }),\r\n canSet: canSetLink(editor),\r\n isActive: isLinkActive(editor),\r\n isFocused: !!editor?.isFocused,\r\n }),\r\n { deps: props.hideWhenUnavailable }\r\n )\r\n\r\n return toStateRefs(state)\r\n}\r\n\r\n/**\r\n * Link popover state and actions.\r\n */\r\nexport function useLinkPopover(config: MaybeGetters<UseLinkPopoverConfig> = {}) {\r\n const editor = useTiptapEditor(config.editor)\r\n\r\n const linkState = useLinkState({\r\n editor,\r\n hideWhenUnavailable: () => resolveValue(config.hideWhenUnavailable) ?? false,\r\n })\r\n\r\n const linkHandler = useLinkHandler({\r\n editor,\r\n onSetLink: config.onSetLink,\r\n })\r\n\r\n return {\r\n ...linkState,\r\n shortcutKeys: LINK_SHORTCUT_KEY,\r\n icon: LinkIcon,\r\n ...linkHandler,\r\n }\r\n}\r\n","<template>\r\n <Button\r\n type=\"button\"\r\n role=\"button\"\r\n :tabindex=\"-1\"\r\n @click=\"emit('click', $event)\"\r\n :aria-label=\"t.link.button\"\r\n :tooltip=\"t.link.button\"\r\n >\r\n <slot>\r\n <Icon :name=\"LinkIcon\" class=\"tiptap-button-icon\" />\r\n </slot>\r\n </Button>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { useTranslations } from \"@/composables/use-translations\"\r\nimport { Button } from \"@/components/tiptap-ui-primitive/button\"\r\nimport Icon from \"@/components/tiptap-icons/Icon.vue\"\r\nimport { LinkIcon } from \"@/components/tiptap-icons\"\r\n\r\nconst t = useTranslations()\r\nconst emit = defineEmits<{ (e: \"click\", event: MouseEvent): void }>()\r\n</script>\r\n","<template>\r\n <input\r\n data-slot=\"tiptap-input\"\r\n class=\"tiptap-input\"\r\n :value=\"modelValue\"\r\n @input=\"onInput\"\r\n @change=\"emit('change', $event)\"\r\n @keydown=\"emit('keydown', $event)\"\r\n />\r\n</template>\r\n\r\n<script lang=\"ts\">\r\n// Vue 2: `v-model` uses modelValue / update:modelValue, like in Vue 3\r\nexport default { model: { prop: \"modelValue\", event: \"update:modelValue\" } }\r\n</script>\r\n\r\n<script setup lang=\"ts\">\r\nimport \"@/components/tiptap-ui-primitive/input/input.scss\"\r\n\r\ndefineProps<{ modelValue?: string }>()\r\n// Emitted (not passed through) so listeners also work in Vue 2\r\nconst emit = defineEmits<{\r\n (e: \"update:modelValue\", value: string): void\r\n (e: \"change\", event: Event): void\r\n (e: \"keydown\", event: KeyboardEvent): void\r\n}>()\r\n\r\nconst onInput = (event: Event) =>\r\n emit(\"update:modelValue\", (event.target as HTMLInputElement).value)\r\n</script>\r\n","<template>\n <Card :style=\"isMobile ? { boxShadow: 'none', border: 0 } : {}\">\n <CardBody :style=\"isMobile ? { padding: 0 } : {}\">\n <CardItemGroup orientation=\"horizontal\">\n <Input\n ref=\"inputRef\"\n :model-value=\"url\"\n type=\"url\"\n :placeholder=\"t.link.placeholder\"\n autofocus\n autocomplete=\"off\"\n autocorrect=\"off\"\n autocapitalize=\"off\"\n class=\"tiptap-link-input\"\n @update:modelValue=\"emit('update:url', $event)\"\n @keydown.enter.prevent=\"emit('set-link')\"\n />\n\n <ButtonGroup>\n <Button\n type=\"button\"\n :title=\"t.link.apply\"\n :disabled=\"!url && !isActive\"\n @click=\"emit('set-link')\"\n >\n <Icon :name=\"CornerDownLeftIcon\" class=\"tiptap-button-icon\" />\n </Button>\n </ButtonGroup>\n\n <!-- p5: open in a new tab or the same tab -->\n <ButtonGroup>\n <Button\n type=\"button\"\n :title=\"t.link.newTab\"\n :aria-pressed=\"String(openInNewTab)\"\n :data-active-state=\"openInNewTab ? 'on' : 'off'\"\n @click=\"emit('toggle-new-tab')\"\n >\n <Icon :name=\"ExternalLinkIcon\" class=\"tiptap-button-icon\" />\n </Button>\n </ButtonGroup>\n\n <Separator />\n\n <ButtonGroup>\n <ButtonGroup>\n <Button\n type=\"button\"\n :title=\"t.link.open\"\n :disabled=\"!url && !isActive\"\n @click=\"emit('open-link')\"\n >\n <Icon :name=\"EyeIcon\" class=\"tiptap-button-icon\" />\n </Button>\n </ButtonGroup>\n\n <ButtonGroup>\n <Button\n type=\"button\"\n :title=\"t.link.remove\"\n :disabled=\"!url && !isActive\"\n @click=\"emit('remove-link')\"\n >\n <Icon :name=\"TrashIcon\" class=\"tiptap-button-icon\" />\n </Button>\n </ButtonGroup>\n </ButtonGroup>\n </CardItemGroup>\n </CardBody>\n </Card>\n</template>\n\n<script setup lang=\"ts\">\nimport { useTranslations } from \"@/composables/use-translations\"\nimport { onMounted, ref } from \"vue\"\nimport { useIsBreakpoint } from \"@/composables/use-is-breakpoint\"\nimport { Button } from \"@/components/tiptap-ui-primitive/button\"\nimport { ButtonGroup } from \"@/components/tiptap-ui-primitive/button-group\"\nimport { Card, CardBody, CardItemGroup } from \"@/components/tiptap-ui-primitive/card\"\nimport { Input } from \"@/components/tiptap-ui-primitive/input\"\nimport { Separator } from \"@/components/tiptap-ui-primitive/separator\"\nimport Icon from \"@/components/tiptap-icons/Icon.vue\"\nimport {\n CornerDownLeftIcon,\n ExternalLinkIcon,\n EyeIcon,\n TrashIcon,\n} from \"@/components/tiptap-icons\"\nimport \"@/components/tiptap-ui/link-popover/link-popover.scss\"\n\nconst t = useTranslations()\n\n/**\n * The URL input and actions shown in the link popover (or mobile toolbar).\n */\ndefineProps<{\n url: string\n /** Whether the cursor is currently in a link */\n isActive: boolean\n /** p5: whether the link opens in a new tab */\n openInNewTab: boolean\n}>()\n\nconst emit = defineEmits<{\n (e: \"update:url\", value: string): void\n (e: \"set-link\"): void\n (e: \"remove-link\"): void\n (e: \"open-link\"): void\n (e: \"toggle-new-tab\"): void\n}>()\n\nconst isMobile = useIsBreakpoint()\n\n// Browsers ignore autofocus on elements added after page load, so focus on\n// mount like React's autoFocus does (the mobile toolbar relies on this). Inside\n// a popover that isn't shown yet this does nothing, and the popover focuses the\n// [autofocus] element itself once shown.\nconst inputRef = ref<{ $el: HTMLInputElement } | null>(null)\nonMounted(() => inputRef.value?.$el.focus())\n</script>\n","<template>\r\n <Popover v-if=\"isVisible\" :open=\"isOpen\" @update:open=\"isOpen = $event\">\r\n <template #trigger=\"{ attrs }\">\r\n <LinkButton\r\n v-bind=\"attrs\"\r\n :disabled=\"!canSet\"\r\n :data-active-state=\"isActive ? 'on' : 'off'\"\r\n :data-disabled=\"!canSet\"\r\n :aria-label=\"t.link.button\"\r\n :aria-pressed=\"String(isActive)\"\r\n :shortcut-keys=\"shortcutKeys\"\r\n :data-rt-shortcut=\"shortcutKeys\"\r\n >\r\n <slot>\r\n <Icon :name=\"icon\" class=\"tiptap-button-icon\" />\r\n </slot>\r\n </LinkButton>\r\n </template>\r\n\r\n <div @keydown=\"onKeydown\">\r\n <LinkMain\r\n :url=\"url\"\r\n :is-active=\"isActive\"\r\n :open-in-new-tab=\"openInNewTab\"\r\n @update:url=\"url = $event\"\r\n @toggle-new-tab=\"toggleNewTab\"\r\n @set-link=\"handleSetLink\"\r\n @remove-link=\"removeLink\"\r\n @open-link=\"openLink()\"\r\n />\r\n </div>\r\n </Popover>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { useTranslations } from \"@/composables/use-translations\"\r\nimport { computed, ref, watch } from \"vue\"\r\nimport type { Editor } from \"@tiptap/core\"\r\nimport { useTiptapEditor } from \"@/composables/use-tiptap-editor\"\r\nimport { matchesShortcut } from \"@/lib/shortcuts\"\r\nimport { useLinkPopover } from \"@/components/tiptap-ui/link-popover/use-link-popover\"\r\nimport { Popover } from \"@/components/tiptap-ui-primitive/popover\"\r\nimport Icon from \"@/components/tiptap-icons/Icon.vue\"\r\nimport LinkButton from \"./LinkButton.vue\"\r\nimport LinkMain from \"./LinkMain.vue\"\r\n\r\nconst t = useTranslations()\r\n\r\n/**\r\n * Link button with a popover for editing the link.\r\n * For custom popovers, use the `useLinkPopover` composable.\r\n *\r\n * The trigger toggles the popover natively (popovertarget), so unlike the\r\n * React template there's no click handler that flips `isOpen`.\r\n *\r\n * p5: Ctrl+K / ⌘K opens it (InputRichtext clicks triggers with `data-rt-shortcut`)\r\n * and closes it again from inside the popover.\r\n */\r\nconst props = withDefaults(\r\n defineProps<{\r\n editor?: Editor | null\r\n hideWhenUnavailable?: boolean\r\n /** Open the popover when the cursor moves into a link */\r\n autoOpenOnLinkActive?: boolean\r\n }>(),\r\n { hideWhenUnavailable: false, autoOpenOnLinkActive: true }\r\n)\r\n\r\nconst emit = defineEmits<{\r\n (e: \"set-link\"): void\r\n (e: \"open-change\", isOpen: boolean): void\r\n}>()\r\n\r\nconst editor = useTiptapEditor(() => props.editor)\r\nconst isOpen = ref(false)\r\n\r\nconst {\r\n isVisible,\r\n canSet,\r\n isActive,\r\n isFocused,\r\n url,\r\n openInNewTab,\r\n toggleNewTab,\r\n setLink,\r\n removeLink,\r\n openLink,\r\n shortcutKeys,\r\n icon,\r\n} = useLinkPopover({\r\n editor,\r\n hideWhenUnavailable: () => props.hideWhenUnavailable,\r\n onSetLink: () => emit(\"set-link\"),\r\n})\r\n\r\nconst shouldAutoOpen = computed(\r\n () => props.autoOpenOnLinkActive && isFocused.value && isActive.value\r\n)\r\n\r\nwatch(shouldAutoOpen, (value) => {\r\n if (value) isOpen.value = true\r\n})\r\n\r\nwatch(isOpen, (open) => emit(\"open-change\", open))\r\n\r\nfunction onKeydown(event: KeyboardEvent) {\r\n if (!matchesShortcut(event, shortcutKeys)) return\r\n event.preventDefault()\r\n isOpen.value = false\r\n}\r\n\r\nfunction handleSetLink() {\r\n setLink()\r\n isOpen.value = false\r\n}\r\n</script>\r\n","<template>\r\n <LinkMain\r\n :url=\"url\"\r\n @update:url=\"url = $event\"\r\n :is-active=\"isActive\"\r\n :open-in-new-tab=\"openInNewTab\"\r\n @toggle-new-tab=\"toggleNewTab\"\r\n @set-link=\"setLink\"\r\n @remove-link=\"removeLink\"\r\n @open-link=\"openLink()\"\r\n />\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport type { Editor } from \"@tiptap/core\"\r\nimport { useLinkPopover } from \"@/components/tiptap-ui/link-popover/use-link-popover\"\r\nimport LinkMain from \"./LinkMain.vue\"\r\n\r\n/**\r\n * Link editing content for standalone use, e.g. in the mobile toolbar.\r\n */\r\nconst props = defineProps<{ editor?: Editor | null }>()\r\n\r\nconst { url, isActive, openInNewTab, toggleNewTab, setLink, removeLink, openLink } = useLinkPopover({\r\n editor: () => props.editor,\r\n})\r\n</script>\r\n","import { defineCommand } from \"@/components/tiptap-ui/command\"\r\nimport type { Editor } from \"@tiptap/core\"\r\n\r\n// --- Lib ---\r\nimport { isMarkInSchema, isNodeTypeSelected } from \"@/lib/tiptap-utils\"\r\n\r\n// --- Icons ---\r\nimport { BoldIcon, Code2Icon, ItalicIcon, StrikeIcon, SubscriptIcon, SuperscriptIcon, UnderlineIcon } from \"@/components/tiptap-icons\"\r\n\r\nexport type Mark =\r\n | \"bold\"\r\n | \"italic\"\r\n | \"strike\"\r\n | \"code\"\r\n | \"underline\"\r\n | \"superscript\"\r\n | \"subscript\"\r\n\r\nexport const markIcons = {\r\n bold: BoldIcon,\r\n italic: ItalicIcon,\r\n underline: UnderlineIcon,\r\n strike: StrikeIcon,\r\n code: Code2Icon,\r\n superscript: SuperscriptIcon,\r\n subscript: SubscriptIcon,\r\n}\r\n\r\nexport const MARK_SHORTCUT_KEYS: Record<Mark, string> = {\r\n bold: \"mod+b\",\r\n italic: \"mod+i\",\r\n underline: \"mod+u\",\r\n strike: \"mod+shift+s\",\r\n code: \"mod+e\",\r\n superscript: \"mod+.\",\r\n subscript: \"mod+,\",\r\n}\r\n\r\n/**\r\n * Checks if a mark can be toggled in the current editor state\r\n */\r\nexport function canToggleMark(editor: Editor | null, type: Mark): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n if (!isMarkInSchema(type, editor) || isNodeTypeSelected(editor, [\"image\"]))\r\n return false\r\n\r\n return editor.can().toggleMark(type)\r\n}\r\n\r\n/**\r\n * Checks if a mark is currently active\r\n */\r\nexport function isMarkActive(editor: Editor | null, type: Mark): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n return editor.isActive(type)\r\n}\r\n\r\n/**\r\n * Toggles a mark in the editor\r\n */\r\nexport function toggleMark(editor: Editor | null, type: Mark): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n if (!canToggleMark(editor, type)) return false\r\n\r\n return editor.chain().focus().toggleMark(type).run()\r\n}\r\n\r\n/**\r\n * Gets the formatted mark name\r\n */\r\n/** p5: toolbar command, rendered by CommandButton (see ../command.ts) */\r\nexport const markCommand = (type: Mark) =>\r\n defineCommand(`mark:${type}`, () => ({\r\n label: (t) => t.marks[type],\r\n icon: markIcons[type],\r\n shortcutKeys: MARK_SHORTCUT_KEYS[type],\r\n isActive: (editor) => isMarkActive(editor, type),\r\n canRun: (editor) => canToggleMark(editor, type),\r\n run: (editor) => toggleMark(editor, type),\r\n }))\r\n","import { defineCommand } from \"@/components/tiptap-ui/command\"\r\nimport type { ChainedCommands } from \"@tiptap/core\"\r\nimport { type Editor } from \"@tiptap/core\"\r\n\r\n// --- Lib ---\r\nimport {\r\n isExtensionAvailable,\r\n isNodeTypeSelected,\r\n} from \"@/lib/tiptap-utils\"\r\n\r\n// --- Icons ---\r\nimport { AlignCenterIcon, AlignJustifyIcon, AlignLeftIcon, AlignRightIcon } from \"@/components/tiptap-icons\"\r\n\r\nexport type TextAlign = \"left\" | \"center\" | \"right\" | \"justify\"\r\n\r\nexport const TEXT_ALIGN_SHORTCUT_KEYS: Record<TextAlign, string> = {\r\n left: \"mod+shift+l\",\r\n center: \"mod+shift+e\",\r\n right: \"mod+shift+r\",\r\n justify: \"mod+shift+j\",\r\n}\r\n\r\nexport const textAlignIcons = {\r\n left: AlignLeftIcon,\r\n center: AlignCenterIcon,\r\n right: AlignRightIcon,\r\n justify: AlignJustifyIcon,\r\n}\r\n\r\n/**\r\n * Checks if text alignment can be performed in the current editor state\r\n */\r\nexport function canSetTextAlign(\r\n editor: Editor | null,\r\n align: TextAlign\r\n): boolean {\r\n if (!editor || editor.isDestroyed || !editor.isEditable) return false\r\n if (\r\n !isExtensionAvailable(editor, \"textAlign\") ||\r\n isNodeTypeSelected(editor, [\"image\", \"horizontalRule\"])\r\n )\r\n return false\r\n\r\n try {\r\n return editor.can().setTextAlign(align)\r\n } catch {\r\n return false\r\n }\r\n}\r\n\r\nexport function hasSetTextAlign(\r\n commands: ChainedCommands\r\n): commands is ChainedCommands & {\r\n setTextAlign: (align: TextAlign) => ChainedCommands\r\n} {\r\n return \"setTextAlign\" in commands\r\n}\r\n\r\n/**\r\n * Checks if the text alignment is currently active\r\n */\r\nexport function isTextAlignActive(\r\n editor: Editor | null,\r\n align: TextAlign\r\n): boolean {\r\n if (!editor || !editor.isEditable) return false\r\n return editor.isActive({ textAlign: align })\r\n}\r\n\r\n/**\r\n * Sets text alignment in the editor\r\n */\r\nexport function setTextAlign(editor: Editor | null, align: TextAlign): boolean {\r\n if (!editor || editor.isDestroyed || !editor.isEditable) return false\r\n if (!canSetTextAlign(editor, align)) return false\r\n\r\n try {\r\n const chain = editor.chain().focus()\r\n if (hasSetTextAlign(chain)) {\r\n return chain.setTextAlign(align).run()\r\n }\r\n } catch {\r\n return false\r\n }\r\n\r\n return false\r\n}\r\n\r\n\r\n/** p5: toolbar command, rendered by CommandButton (see ../command.ts) */\r\nexport const textAlignCommand = (align: TextAlign) =>\r\n defineCommand(`textAlign:${align}`, () => ({\r\n label: (t) => t.align[align],\r\n icon: textAlignIcons[align],\r\n shortcutKeys: TEXT_ALIGN_SHORTCUT_KEYS[align],\r\n isActive: (editor) => isTextAlignActive(editor, align),\r\n canRun: (editor) => canSetTextAlign(editor, align),\r\n run: (editor) => setTextAlign(editor, align),\r\n }))\r\n","import { defineCommand } from \"@/components/tiptap-ui/command\"\r\nimport { type Editor } from \"@tiptap/core\"\r\n\r\n// --- Lib ---\r\nimport { isNodeTypeSelected } from \"@/lib/tiptap-utils\"\r\n\r\n// --- Icons ---\r\nimport { Redo2Icon, Undo2Icon } from \"@/components/tiptap-icons\"\r\n\r\nexport type UndoRedoAction = \"undo\" | \"redo\"\r\n\r\nexport const UNDO_REDO_SHORTCUT_KEYS: Record<UndoRedoAction, string> = {\r\n undo: \"mod+z\",\r\n redo: \"mod+shift+z\",\r\n}\r\n\r\nexport const historyIcons = {\r\n undo: Undo2Icon,\r\n redo: Redo2Icon,\r\n}\r\n\r\n/**\r\n * Checks if a history action can be executed\r\n */\r\nexport function canExecuteUndoRedoAction(\r\n editor: Editor | null,\r\n action: UndoRedoAction\r\n): boolean {\r\n if (!editor || editor.isDestroyed || !editor.isEditable) return false\r\n\r\n try {\r\n if (isNodeTypeSelected(editor, [\"image\"])) return false\r\n\r\n const commands = editor.can()\r\n const command = action === \"undo\" ? commands.undo : commands.redo\r\n\r\n return typeof command === \"function\" ? command() : false\r\n } catch {\r\n return false\r\n }\r\n}\r\n\r\n/**\r\n * Executes a history action on the editor\r\n */\r\nexport function executeUndoRedoAction(\r\n editor: Editor | null,\r\n action: UndoRedoAction\r\n): boolean {\r\n if (!editor || editor.isDestroyed || !editor.isEditable) return false\r\n if (!canExecuteUndoRedoAction(editor, action)) return false\r\n\r\n try {\r\n const chain = editor.chain().focus()\r\n return action === \"undo\" ? chain.undo().run() : chain.redo().run()\r\n } catch {\r\n return false\r\n }\r\n}\r\n\r\n\r\n/** p5: toolbar command, rendered by CommandButton (see ../command.ts) */\r\nexport const undoRedoCommand = (action: UndoRedoAction) =>\r\n defineCommand(`history:${action}`, () => ({\r\n label: (t) => t[action],\r\n icon: historyIcons[action],\r\n shortcutKeys: UNDO_REDO_SHORTCUT_KEYS[action],\r\n canRun: (editor) => canExecuteUndoRedoAction(editor, action),\r\n run: (editor) => executeUndoRedoAction(editor, action),\r\n }))\r\n","import { watch, type Ref } from \"vue\"\n\n/**\n * p5: shows an element with `popover=\"manual\"` in the browser's top layer as\n * soon as it's rendered, so it isn't hidden behind modal <dialog>s or a\n * fullscreen element (which are in the top layer too). `onShown` runs\n * afterwards, e.g. to measure it: a popover has no size until it's shown.\n * Without popover support the element simply stays where it is.\n */\nexport function useTopLayer(element: Ref<HTMLElement | null>, onShown?: () => void) {\n watch(\n element,\n (el) => {\n if (!el) return\n if (!el.matches(\":popover-open\")) el.showPopover?.()\n onShown?.()\n },\n { flush: \"post\" }\n )\n}\n","<template>\r\n <Portal>\r\n <div\r\n v-if=\"state.open\"\r\n ref=\"menuRef\"\r\n popover=\"manual\"\r\n class=\"tiptap-dropdown-menu-content rt-mention-menu\"\r\n data-state=\"open\"\r\n role=\"listbox\"\r\n :style=\"position\"\r\n >\r\n <div v-if=\"!state.items.length\" class=\"rt-mention-menu-empty\">{{ t.mentions.noResults }}</div>\r\n <div v-else class=\"tiptap-dropdown-menu-group\">\r\n <template v-for=\"(item, index) in state.items\">\r\n <div\r\n v-if=\"item.group && item.group !== state.items[index - 1]?.group\"\r\n class=\"tiptap-dropdown-menu-label\"\r\n >\r\n {{ item.group }}\r\n </div>\r\n <Button\r\n type=\"button\"\r\n role=\"option\"\r\n :aria-selected=\"String(index === state.selectedIndex)\"\r\n :data-highlighted=\"index === state.selectedIndex\"\r\n @mousedown.prevent\r\n @click=\"emit('select', index)\"\r\n >\r\n <span class=\"tiptap-button-text\">{{ item.label }}</span>\r\n </Button>\r\n </template>\r\n </div>\r\n </div>\r\n </Portal>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport Portal from \"@/components/tiptap-ui-primitive/portal/Portal.vue\"\r\nimport { useTranslations } from \"@/composables/use-translations\"\r\nimport { computed, nextTick, ref, watch } from \"vue\"\r\nimport { useTopLayer } from \"@/composables/use-top-layer\"\r\nimport { Button } from \"@/components/tiptap-ui-primitive/button\"\r\nimport type { MentionMenuState } from \"./extensions/mention-menu\"\r\n\r\nconst t = useTranslations()\r\n\r\n/**\r\n * Suggestion list for mentions, positioned under the typed \"@query\"\r\n * (or above it when there's no room below).\r\n */\r\nconst props = defineProps<{ state: MentionMenuState }>()\r\nconst emit = defineEmits<{ (e: \"select\", index: number): void }>()\r\n\r\nconst OFFSET = 4\r\nconst menuRef = ref<HTMLElement | null>(null)\r\nconst menuSize = ref({ width: 0, height: 0 })\r\n\r\nconst measure = () => {\r\n menuSize.value = {\r\n width: menuRef.value?.offsetWidth ?? 0,\r\n height: menuRef.value?.offsetHeight ?? 0,\r\n }\r\n}\r\n\r\n// In the top layer, so it also shows in modals and fullscreen\r\nuseTopLayer(menuRef, measure)\r\n\r\nwatch(\r\n () => [props.state.open, props.state.items],\r\n async () => {\r\n await nextTick()\r\n measure()\r\n }\r\n)\r\n\r\nwatch(\r\n () => props.state.selectedIndex,\r\n async () => {\r\n await nextTick()\r\n menuRef.value\r\n ?.querySelector('[data-highlighted=\"true\"]')\r\n ?.scrollIntoView({ block: \"nearest\" })\r\n }\r\n)\r\n\r\nconst position = computed(() => {\r\n const rect = props.state.rect\r\n if (!rect) return { visibility: \"hidden\" as const }\r\n\r\n const { width, height } = menuSize.value\r\n const fitsBelow = rect.bottom + OFFSET + height <= window.innerHeight\r\n return {\r\n top: `${fitsBelow ? rect.bottom + OFFSET : Math.max(OFFSET, rect.top - OFFSET - height)}px`,\r\n left: `${Math.max(OFFSET, Math.min(rect.left, window.innerWidth - width - OFFSET))}px`,\r\n }\r\n})\r\n</script>\r\n","import type { Editor } from \"@tiptap/core\"\n\n/**\n * Transaction meta that makes the decorations based on `mentionPreviews`\n * (mention values, hidden visibility conditions) read the previews again.\n */\nexport const PREVIEWS_CHANGED = \"rt-previews-changed\"\n\n/** Call after the preview values changed */\nexport function refreshPreviews(editor: Editor) {\n editor.view.dispatch(editor.state.tr.setMeta(PREVIEWS_CHANGED, true))\n}\n","import { mergeAttributes, Node } from \"@tiptap/core\"\r\nimport type { Node as PMNode } from \"@tiptap/pm/model\"\r\nimport { Plugin, PluginKey } from \"@tiptap/pm/state\"\r\nimport { Decoration, DecorationSet } from \"@tiptap/pm/view\"\r\nimport { DEFAULT_LOCALE, locales, type RichtextMessages } from \"@/locales\"\r\nimport { PREVIEWS_CHANGED } from \"./previews\"\r\nimport type { VisibilityCondition } from \"../types\"\r\n\r\ndeclare module \"@tiptap/core\" {\r\n interface Commands<ReturnType> {\r\n visibilityConditions: {\r\n /**\r\n * Wraps the selected blocks in the condition. Removes the wrapper when the\r\n * selection is already in that condition, or switches to it when the\r\n * selection is in another condition.\r\n */\r\n toggleCondition: (condition: VisibilityCondition) => ReturnType\r\n /** Removes the condition wrapper around the selection */\r\n unsetCondition: () => ReturnType\r\n }\r\n }\r\n}\r\n\r\nexport const VISIBILITY_CONDITION_NODE = \"visibilityConditions\"\r\n\r\nexport interface VisibilityConditionsOptions {\r\n /** `mentionPreviews`, which can hold the outcome of a condition, keyed by its `if` */\r\n getPreviews: () => Record<string, unknown>\r\n messages: () => RichtextMessages\r\n}\r\n\r\nconst hiddenKey = new PluginKey<DecorationSet>(\"hiddenConditions\")\r\n\r\n/** A preview of false, 0, \"\", \"0\" or \"false\" fails; without a preview the outcome is unknown */\r\nfunction conditionPasses(value: unknown): boolean | undefined {\r\n if (value == null) return undefined\r\n return !(value === false || value === 0 || [\"\", \"0\", \"false\"].includes(String(value).trim()))\r\n}\r\n\r\n/**\r\n * Marks condition blocks whose condition fails with `data-hidden-label`, which\r\n * hides their content in the editor (input-richtext.scss). Like the mention\r\n * previews, this only affects the view: the blocks stay in the value.\r\n */\r\nfunction hiddenDecorations(doc: PMNode, options: VisibilityConditionsOptions) {\r\n const previews = options.getPreviews()\r\n if (!Object.keys(previews).length) return DecorationSet.empty\r\n\r\n const decorations: Decoration[] = []\r\n doc.descendants((node, pos) => {\r\n if (node.type.name !== VISIBILITY_CONDITION_NODE) return\r\n const { condition } = node.attrs\r\n if (!condition || conditionPasses(previews[condition]) !== false) return\r\n const label = node.attrs[\"condition-label\"] || condition\r\n decorations.push(\r\n Decoration.node(pos, pos + node.nodeSize, {\r\n \"data-hidden-label\": options.messages().visibilityConditions.hidden(label),\r\n })\r\n )\r\n })\r\n return DecorationSet.create(doc, decorations)\r\n}\r\n\r\n/**\r\n * Block that is only shown when a condition is met, like a blockquote wraps\r\n * other blocks. Stored as\r\n * `<div class=\"conditionally-visible\" condition=\"…\" condition-label=\"…\">`.\r\n *\r\n * The HTML output wraps it in liquid tags, see ../liquid.ts.\r\n * The node name matches v-richtext 2.x, so JSON content stays compatible.\r\n */\r\nexport const VisibilityConditions = Node.create<VisibilityConditionsOptions>({\r\n name: VISIBILITY_CONDITION_NODE,\r\n group: \"block\",\r\n content: \"block+\",\r\n defining: true,\r\n\r\n addOptions() {\r\n return {\r\n getPreviews: () => ({}),\r\n messages: () => locales[DEFAULT_LOCALE],\r\n }\r\n },\r\n\r\n addAttributes() {\r\n return {\r\n condition: {\r\n default: null,\r\n parseHTML: (element) => element.getAttribute(\"condition\"),\r\n renderHTML: (attributes) =>\r\n attributes.condition ? { condition: attributes.condition } : {},\r\n },\r\n \"condition-label\": {\r\n default: null,\r\n parseHTML: (element) => element.getAttribute(\"condition-label\"),\r\n renderHTML: (attributes) =>\r\n attributes[\"condition-label\"]\r\n ? { \"condition-label\": attributes[\"condition-label\"] }\r\n : {},\r\n },\r\n }\r\n },\r\n\r\n parseHTML() {\r\n return [{ tag: \"div.conditionally-visible\" }]\r\n },\r\n\r\n renderHTML({ HTMLAttributes }) {\r\n return [\r\n \"div\",\r\n mergeAttributes(HTMLAttributes, { class: \"conditionally-visible\" }),\r\n 0,\r\n ]\r\n },\r\n\r\n addCommands() {\r\n return {\r\n toggleCondition:\r\n (condition) =>\r\n ({ editor, commands }) => {\r\n const attrs = {\r\n condition: condition.if,\r\n \"condition-label\": condition.label,\r\n }\r\n\r\n if (editor.isActive(this.name, { condition: condition.if })) {\r\n return commands.lift(this.name)\r\n }\r\n if (editor.isActive(this.name)) {\r\n return commands.updateAttributes(this.name, attrs)\r\n }\r\n return commands.wrapIn(this.name, attrs)\r\n },\r\n\r\n unsetCondition:\r\n () =>\r\n ({ commands }) =>\r\n commands.lift(this.name),\r\n }\r\n },\r\n\r\n addProseMirrorPlugins() {\r\n return [\r\n new Plugin({\r\n key: hiddenKey,\r\n state: {\r\n init: (_, state) => hiddenDecorations(state.doc, this.options),\r\n apply: (tr, decorations) =>\r\n tr.docChanged || tr.getMeta(PREVIEWS_CHANGED)\r\n ? hiddenDecorations(tr.doc, this.options)\r\n : decorations,\r\n },\r\n props: {\r\n decorations: (state) => hiddenKey.getState(state),\r\n },\r\n }),\r\n ]\r\n },\r\n})\r\n","<template>\r\n <DropdownMenu v-if=\"conditions.length\" :open=\"isOpen\" @update:open=\"isOpen = $event\" align=\"start\">\r\n <template #trigger=\"{ attrs }\">\r\n <Button\r\n v-bind=\"attrs\"\r\n type=\"button\"\r\n role=\"button\"\r\n :tabindex=\"-1\"\r\n :disabled=\"!canToggle\"\r\n :data-disabled=\"!canToggle\"\r\n :data-active-state=\"activeCondition ? 'on' : 'off'\"\r\n :aria-pressed=\"String(!!activeCondition)\"\r\n :aria-label=\"t.visibilityConditions.button\"\r\n :tooltip=\"t.visibilityConditions.button\"\r\n :shortcut-keys=\"SHORTCUTS.visibilityCondition\"\r\n :data-rt-shortcut=\"SHORTCUTS.visibilityCondition\"\r\n >\r\n <Icon :name=\"EyeIcon\" class=\"tiptap-button-icon\" />\r\n <Icon :name=\"ChevronDownIcon\" class=\"tiptap-button-dropdown-small\" />\r\n </Button>\r\n </template>\r\n\r\n <div class=\"tiptap-dropdown-menu-group\">\r\n <Button\r\n v-for=\"condition in conditions\"\r\n :key=\"condition.if\"\r\n type=\"button\"\r\n role=\"menuitem\"\r\n :data-active-state=\"activeCondition === condition.if ? 'on' : 'off'\"\r\n @click=\"editor?.chain().focus().toggleCondition(condition).run()\"\r\n >\r\n <span class=\"tiptap-button-text\">{{ condition.label }}</span>\r\n </Button>\r\n <Button\r\n v-if=\"activeCondition\"\r\n type=\"button\"\r\n role=\"menuitem\"\r\n @click=\"editor?.chain().focus().unsetCondition().run()\"\r\n >\r\n <Icon :name=\"BanIcon\" class=\"tiptap-button-icon\" />\r\n <span class=\"tiptap-button-text\">{{ t.visibilityConditions.remove }}</span>\r\n </Button>\r\n </div>\r\n </DropdownMenu>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { useTranslations } from \"@/composables/use-translations\"\r\nimport { ref } from \"vue\"\r\nimport type { Editor } from \"@tiptap/core\"\r\nimport { useTiptapEditor } from \"@/composables/use-tiptap-editor\"\r\nimport { useEditorState, toStateRefs } from \"@/composables/use-editor-state\"\r\nimport { Button } from \"@/components/tiptap-ui-primitive/button\"\r\nimport { DropdownMenu } from \"@/components/tiptap-ui-primitive/dropdown-menu\"\r\nimport Icon from \"@/components/tiptap-icons/Icon.vue\"\r\nimport { BanIcon, ChevronDownIcon, EyeIcon } from \"@/components/tiptap-icons\"\r\nimport { VISIBILITY_CONDITION_NODE } from \"./extensions/visibility-conditions\"\r\nimport { SHORTCUTS } from \"@/lib/shortcuts\"\r\nimport type { VisibilityCondition } from \"./types\"\r\n\r\nconst t = useTranslations()\r\n\r\n/**\r\n * Dropdown to wrap the selected blocks in a visibility condition, like a blockquote.\r\n */\r\nconst props = defineProps<{\r\n editor?: Editor | null\r\n conditions: VisibilityCondition[]\r\n}>()\r\n\r\nconst editor = useTiptapEditor(() => props.editor)\r\nconst isOpen = ref(false)\r\n\r\nconst { activeCondition, canToggle } = toStateRefs(\r\n useEditorState(editor, (editor) => {\r\n const isActive = !!editor?.isActive(VISIBILITY_CONDITION_NODE)\r\n return {\r\n activeCondition: isActive\r\n ? (editor!.getAttributes(VISIBILITY_CONDITION_NODE).condition as string | null)\r\n : null,\r\n canToggle:\r\n !!editor?.isEditable &&\r\n (isActive || editor.can().wrapIn(VISIBILITY_CONDITION_NODE)),\r\n }\r\n })\r\n)\r\n</script>\r\n","import { Node } from \"@tiptap/core\"\r\n\r\ndeclare module \"@tiptap/core\" {\r\n interface Commands<ReturnType> {\r\n file: {\r\n /** Inserts a file link, or replaces the selected one */\r\n setFile: (file: { href: string; name?: string }) => ReturnType\r\n }\r\n }\r\n}\r\n\r\n/** \"https://example.com/files/report.pdf?x=1\" → \"report.pdf\" */\r\nexport function fileNameFromUrl(href: string): string {\r\n try {\r\n const path = new URL(href, window.location.href).pathname\r\n return decodeURIComponent(path.split(\"/\").filter(Boolean).pop() ?? \"\") || href\r\n } catch {\r\n return href\r\n }\r\n}\r\n\r\n/**\r\n * File link block: `<a data-type=\"file\" href=\"…\">name</a>`, same as v-richtext 2.x.\r\n */\r\nexport const FileNode = Node.create({\r\n name: \"file\",\r\n group: \"block\",\r\n content: \"text*\",\r\n selectable: true,\r\n draggable: true,\r\n defining: true,\r\n atom: true,\r\n marks: \"\",\r\n\r\n addAttributes() {\r\n return {\r\n href: { default: null },\r\n title: { default: null },\r\n target: { default: \"_blank\" },\r\n \"data-type\": { default: \"file\" },\r\n }\r\n },\r\n\r\n parseHTML() {\r\n return [{ tag: 'a[data-type=\"file\"]', priority: 100 }]\r\n },\r\n\r\n renderHTML({ HTMLAttributes }) {\r\n const { title, ...attributes } = HTMLAttributes\r\n return [\"a\", attributes, title || 0]\r\n },\r\n\r\n addCommands() {\r\n return {\r\n setFile:\r\n ({ href, name }) =>\r\n ({ commands }) =>\r\n commands.insertContent({\r\n type: this.name,\r\n attrs: { href },\r\n content: [{ type: \"text\", text: name || fileNameFromUrl(href) }],\r\n }),\r\n }\r\n },\r\n})\r\n","<template>\r\n <Popover :open=\"isOpen\" @update:open=\"isOpen = $event\">\r\n <template #trigger=\"{ attrs }\">\r\n <Button\r\n v-bind=\"attrs\"\r\n type=\"button\"\r\n role=\"button\"\r\n :tabindex=\"-1\"\r\n :disabled=\"!canInsert && !isActive\"\r\n :data-disabled=\"!canInsert && !isActive\"\r\n :data-active-state=\"isActive ? 'on' : 'off'\"\r\n :aria-pressed=\"String(isActive)\"\r\n :aria-label=\"text.button\"\r\n :tooltip=\"text.button\"\r\n :shortcut-keys=\"shortcutKeys\"\r\n :data-rt-shortcut=\"shortcutKeys\"\r\n >\r\n <Icon :name=\"icon\" class=\"tiptap-button-icon\" />\r\n </Button>\r\n </template>\r\n\r\n <Card>\r\n <CardBody>\r\n <!-- One is selected -->\r\n <CardItemGroup v-if=\"isActive\" orientation=\"horizontal\">\r\n <span class=\"rt-popover-text\" :title=\"href\">{{ name }}</span>\r\n <Separator />\r\n <ButtonGroup>\r\n <Button type=\"button\" :title=\"text.open\" @click=\"openSelected\">\r\n <Icon :name=\"EyeIcon\" class=\"tiptap-button-icon\" />\r\n </Button>\r\n <Button type=\"button\" :title=\"text.remove\" @click=\"removeSelected\">\r\n <Icon :name=\"TrashIcon\" class=\"tiptap-button-icon\" />\r\n </Button>\r\n </ButtonGroup>\r\n </CardItemGroup>\r\n\r\n <!-- Add one: upload a file, or link to a URL -->\r\n <template v-else>\r\n <CardItemGroup orientation=\"horizontal\">\r\n <template v-if=\"upload\">\r\n <Button\r\n type=\"button\"\r\n class=\"rt-upload-button\"\r\n :disabled=\"isUploading\"\r\n @click=\"fileInput?.click()\"\r\n >\r\n <Icon :name=\"icon\" class=\"tiptap-button-icon\" />\r\n <span class=\"tiptap-button-text\">\r\n {{ isUploading ? t.upload.uploading : text.choose }}\r\n </span>\r\n </Button>\r\n <input\r\n ref=\"fileInput\"\r\n type=\"file\"\r\n :accept=\"type === 'image' ? 'image/*' : undefined\"\r\n hidden\r\n @change=\"onFileChosen\"\r\n />\r\n <Separator />\r\n </template>\r\n <Input\r\n v-model=\"url\"\r\n type=\"url\"\r\n :placeholder=\"upload ? t.upload.orPasteLink : t.upload.pasteLink\"\r\n autofocus\r\n autocomplete=\"off\"\r\n class=\"tiptap-link-input\"\r\n @keydown.enter.prevent=\"insertUrl\"\r\n />\r\n <ButtonGroup>\r\n <Button\r\n type=\"button\"\r\n :title=\"text.add\"\r\n :disabled=\"!url\"\r\n @click=\"insertUrl\"\r\n >\r\n <Icon :name=\"CornerDownLeftIcon\" class=\"tiptap-button-icon\" />\r\n </Button>\r\n </ButtonGroup>\r\n </CardItemGroup>\r\n <p v-if=\"error\" class=\"rt-popover-error\">{{ error }}</p>\r\n </template>\r\n </CardBody>\r\n </Card>\r\n </Popover>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { useTranslations } from \"@/composables/use-translations\"\r\nimport { computed, ref, watch } from \"vue\"\r\nimport { NodeSelection } from \"@tiptap/pm/state\"\r\nimport { useTiptapEditor } from \"@/composables/use-tiptap-editor\"\r\nimport { useEditorState, toStateRefs } from \"@/composables/use-editor-state\"\r\nimport { Button } from \"@/components/tiptap-ui-primitive/button\"\r\nimport { ButtonGroup } from \"@/components/tiptap-ui-primitive/button-group\"\r\nimport { Card, CardBody, CardItemGroup } from \"@/components/tiptap-ui-primitive/card\"\r\nimport { Input } from \"@/components/tiptap-ui-primitive/input\"\r\nimport { Popover } from \"@/components/tiptap-ui-primitive/popover\"\r\nimport { Separator } from \"@/components/tiptap-ui-primitive/separator\"\r\nimport Icon from \"@/components/tiptap-icons/Icon.vue\"\r\nimport {\r\n CornerDownLeftIcon,\r\n EyeIcon,\r\n ImagePlusIcon,\r\n PaperclipIcon,\r\n TrashIcon,\r\n} from \"@/components/tiptap-icons\"\r\nimport { sanitizeUrl } from \"@/lib/tiptap-utils\"\r\nimport { fileNameFromUrl } from \"./extensions/file-node\"\r\nimport type { UploadFileFunction } from \"./types\"\r\nimport \"@/components/tiptap-ui/link-popover/link-popover.scss\"\r\n\r\nconst t = useTranslations()\r\n\r\n/**\r\n * Toolbar button for images or file links. Uploads a file through `upload`,\r\n * or links to a URL. With one selected, it offers to open or remove it.\r\n */\r\nconst props = defineProps<{\r\n type: \"image\" | \"file\"\r\n /** Without it, only linking to a URL is offered. Checks the size and reports errors itself. */\r\n upload?: UploadFileFunction\r\n /** Opens the popover; see lib/shortcuts.ts */\r\n shortcutKeys?: string\r\n}>()\r\n\r\nconst text = computed(() => t.value[props.type])\r\nconst icon = computed(() => (props.type === \"image\" ? ImagePlusIcon : PaperclipIcon))\r\n\r\nconst editor = useTiptapEditor()\r\nconst isOpen = ref(false)\r\nconst url = ref(\"\")\r\nconst error = ref(\"\")\r\nconst isUploading = ref(false)\r\nconst fileInput = ref<HTMLInputElement | null>(null)\r\n\r\nconst { isActive, href, name, canInsert } = toStateRefs(\r\n useEditorState(\r\n editor,\r\n (editor) => {\r\n const { selection } = editor?.state ?? {}\r\n const node =\r\n selection instanceof NodeSelection && selection.node.type.name === props.type\r\n ? selection.node\r\n : null\r\n const nodeHref = ((props.type === \"image\" ? node?.attrs.src : node?.attrs.href) as string) ?? \"\"\r\n\r\n return {\r\n isActive: !!node,\r\n href: nodeHref,\r\n name:\r\n props.type === \"image\"\r\n ? (node?.attrs.alt as string) || fileNameFromUrl(nodeHref)\r\n : (node?.textContent ?? \"\"),\r\n canInsert:\r\n !!editor?.isEditable &&\r\n (props.type === \"image\"\r\n ? editor.can().setImage({ src: \"#\" })\r\n : editor.can().setFile({ href: \"#\" })),\r\n }\r\n },\r\n { deps: () => props.type }\r\n )\r\n)\r\n\r\nwatch(isOpen, (open) => {\r\n if (!open) return\r\n url.value = \"\"\r\n error.value = \"\"\r\n})\r\n\r\nfunction insert(href: string, name?: string) {\r\n const chain = editor.value?.chain().focus()\r\n if (props.type === \"image\") {\r\n chain?.setImage({ src: href, alt: name?.replace(/\\.[^/.]+$/, \"\") }).run()\r\n } else {\r\n chain?.setFile({ href, name }).run()\r\n }\r\n isOpen.value = false\r\n}\r\n\r\nfunction insertUrl() {\r\n if (url.value) insert(url.value)\r\n}\r\n\r\nasync function onFileChosen(event: Event) {\r\n const input = event.target as HTMLInputElement\r\n const file = input.files?.[0]\r\n input.value = \"\"\r\n if (!file || !props.upload) return\r\n\r\n error.value = \"\"\r\n isUploading.value = true\r\n try {\r\n insert(await props.upload(file, props.type), file.name)\r\n } catch (uploadError) {\r\n error.value = uploadError instanceof Error ? uploadError.message : t.value.upload.failed\r\n } finally {\r\n isUploading.value = false\r\n }\r\n}\r\n\r\nfunction openSelected() {\r\n const safeUrl = sanitizeUrl(href.value, window.location.href)\r\n if (safeUrl !== \"#\") window.open(safeUrl, \"_blank\", \"noopener,noreferrer\")\r\n}\r\n\r\nfunction removeSelected() {\r\n editor.value?.chain().focus().deleteSelection().run()\r\n isOpen.value = false\r\n}\r\n</script>\r\n","<template>\r\n <Popover :open=\"isOpen\" @update:open=\"isOpen = $event\">\r\n <template #trigger=\"{ attrs }\">\r\n <Button\r\n v-bind=\"attrs\"\r\n type=\"button\"\r\n role=\"button\"\r\n :tabindex=\"-1\"\r\n :disabled=\"!canInsert && !isActive\"\r\n :data-disabled=\"!canInsert && !isActive\"\r\n :data-active-state=\"isActive ? 'on' : 'off'\"\r\n :aria-pressed=\"String(isActive)\"\r\n :aria-label=\"t.youtube.button\"\r\n :tooltip=\"t.youtube.button\"\r\n >\r\n <Icon :name=\"YoutubeIcon\" class=\"tiptap-button-icon\" />\r\n </Button>\r\n </template>\r\n\r\n <Card>\r\n <CardBody>\r\n <!-- A video is selected -->\r\n <CardItemGroup v-if=\"isActive\" orientation=\"horizontal\">\r\n <span class=\"rt-popover-text\" :title=\"src\">{{ src }}</span>\r\n <Separator />\r\n <ButtonGroup>\r\n <Button type=\"button\" :title=\"t.youtube.remove\" @click=\"removeVideo\">\r\n <Icon :name=\"TrashIcon\" class=\"tiptap-button-icon\" />\r\n </Button>\r\n </ButtonGroup>\r\n </CardItemGroup>\r\n\r\n <template v-else>\r\n <CardItemGroup orientation=\"horizontal\">\r\n <Input\r\n v-model=\"url\"\r\n type=\"url\"\r\n :placeholder=\"t.youtube.placeholder\"\r\n autofocus\r\n autocomplete=\"off\"\r\n class=\"tiptap-link-input\"\r\n @keydown.enter.prevent=\"insertVideo\"\r\n />\r\n <ButtonGroup>\r\n <Button\r\n type=\"button\"\r\n :title=\"t.youtube.add\"\r\n :disabled=\"!url\"\r\n @click=\"insertVideo\"\r\n >\r\n <Icon :name=\"CornerDownLeftIcon\" class=\"tiptap-button-icon\" />\r\n </Button>\r\n </ButtonGroup>\r\n </CardItemGroup>\r\n <p v-if=\"error\" class=\"rt-popover-error\">{{ error }}</p>\r\n </template>\r\n </CardBody>\r\n </Card>\r\n </Popover>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { useTranslations } from \"@/composables/use-translations\"\r\nimport { ref, watch } from \"vue\"\r\nimport { NodeSelection } from \"@tiptap/pm/state\"\r\nimport { useTiptapEditor } from \"@/composables/use-tiptap-editor\"\r\nimport { useEditorState, toStateRefs } from \"@/composables/use-editor-state\"\r\nimport { Button } from \"@/components/tiptap-ui-primitive/button\"\r\nimport { ButtonGroup } from \"@/components/tiptap-ui-primitive/button-group\"\r\nimport { Card, CardBody, CardItemGroup } from \"@/components/tiptap-ui-primitive/card\"\r\nimport { Input } from \"@/components/tiptap-ui-primitive/input\"\r\nimport { Popover } from \"@/components/tiptap-ui-primitive/popover\"\r\nimport { Separator } from \"@/components/tiptap-ui-primitive/separator\"\r\nimport Icon from \"@/components/tiptap-icons/Icon.vue\"\r\nimport { CornerDownLeftIcon, TrashIcon, YoutubeIcon } from \"@/components/tiptap-icons\"\r\nimport \"@/components/tiptap-ui/link-popover/link-popover.scss\"\r\n\r\nconst t = useTranslations()\r\n\r\n/**\r\n * Toolbar button to embed a YouTube video from its URL.\r\n * With a video selected, it offers to remove it.\r\n */\r\nconst editor = useTiptapEditor()\r\nconst isOpen = ref(false)\r\nconst url = ref(\"\")\r\nconst error = ref(\"\")\r\n\r\nconst { isActive, src, canInsert } = toStateRefs(\r\n useEditorState(editor, (editor) => {\r\n const { selection } = editor?.state ?? {}\r\n const node =\r\n selection instanceof NodeSelection && selection.node.type.name === \"youtube\"\r\n ? selection.node\r\n : null\r\n\r\n return {\r\n isActive: !!node,\r\n src: (node?.attrs.src as string | null) ?? \"\",\r\n canInsert:\r\n !!editor?.isEditable &&\r\n editor.can().insertContent({ type: \"youtube\", attrs: { src: \"#\" } }),\r\n }\r\n })\r\n)\r\n\r\nwatch(isOpen, (open) => {\r\n if (!open) return\r\n url.value = \"\"\r\n error.value = \"\"\r\n})\r\n\r\nfunction insertVideo() {\r\n if (!url.value) return\r\n // Returns false when the URL isn't a YouTube URL\r\n const inserted = editor.value\r\n ?.chain()\r\n .focus()\r\n .setYoutubeVideo({ src: url.value })\r\n .run()\r\n\r\n if (inserted) isOpen.value = false\r\n else error.value = t.value.youtube.invalid\r\n}\r\n\r\nfunction removeVideo() {\r\n editor.value?.chain().focus().deleteSelection().run()\r\n isOpen.value = false\r\n}\r\n</script>\r\n","<template>\r\n <DropdownMenu :open=\"isOpen\" @update:open=\"isOpen = $event\" align=\"start\">\r\n <template #trigger=\"{ attrs }\">\r\n <Button\r\n v-bind=\"attrs\"\r\n type=\"button\"\r\n role=\"button\"\r\n :tabindex=\"-1\"\r\n :disabled=\"!isInTable && !canInsert\"\r\n :data-disabled=\"!isInTable && !canInsert\"\r\n :data-active-state=\"isInTable ? 'on' : 'off'\"\r\n :aria-pressed=\"String(isInTable)\"\r\n :aria-label=\"t.table.button\"\r\n :tooltip=\"t.table.button\"\r\n :shortcut-keys=\"SHORTCUTS.table\"\r\n :data-rt-shortcut=\"SHORTCUTS.table\"\r\n >\r\n <Icon :name=\"TableIcon\" class=\"tiptap-button-icon\" />\r\n <Icon :name=\"ChevronDownIcon\" class=\"tiptap-button-dropdown-small\" />\r\n </Button>\r\n </template>\r\n\r\n <div class=\"tiptap-dropdown-menu-group\">\r\n <template v-for=\"(group, index) in groups\">\r\n <div v-if=\"index > 0\" class=\"tiptap-dropdown-menu-separator\" role=\"separator\" />\r\n <Button\r\n v-for=\"action in group\"\r\n :key=\"action.label\"\r\n type=\"button\"\r\n role=\"menuitem\"\r\n :disabled=\"!action.enabled\"\r\n @click=\"action.run\"\r\n >\r\n <span class=\"tiptap-button-text\">{{ action.label }}</span>\r\n </Button>\r\n </template>\r\n </div>\r\n </DropdownMenu>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { useTranslations } from \"@/composables/use-translations\"\r\nimport { computed, ref } from \"vue\"\r\nimport type { ChainedCommands } from \"@tiptap/core\"\r\nimport { useTiptapEditor } from \"@/composables/use-tiptap-editor\"\r\nimport { useEditorState, toStateRefs } from \"@/composables/use-editor-state\"\r\nimport { Button } from \"@/components/tiptap-ui-primitive/button\"\r\nimport { DropdownMenu } from \"@/components/tiptap-ui-primitive/dropdown-menu\"\r\nimport Icon from \"@/components/tiptap-icons/Icon.vue\"\r\nimport { ChevronDownIcon, TableIcon } from \"@/components/tiptap-icons\"\r\nimport { SHORTCUTS } from \"@/lib/shortcuts\"\r\nimport type { RichtextOptions } from \"./types\"\r\n\r\nconst t = useTranslations()\r\n\r\n/**\r\n * Table dropdown: \"Insert table\" outside a table, row/column/header/merge\r\n * actions inside one.\r\n */\r\nconst props = defineProps<{\r\n insertDefaults?: NonNullable<RichtextOptions[\"table\"]>[\"insertDefaults\"]\r\n}>()\r\n\r\nconst editor = useTiptapEditor()\r\nconst isOpen = ref(false)\r\n\r\nconst insertOptions = computed(\r\n () => props.insertDefaults ?? { rows: 3, cols: 3, withHeaderRow: true }\r\n)\r\n\r\nconst { isInTable, canInsert } = toStateRefs(\r\n useEditorState(editor, (editor) => ({\r\n isInTable: !!editor?.isActive(\"table\"),\r\n canInsert: !!editor?.isEditable && editor.can().insertTable(insertOptions.value),\r\n }))\r\n)\r\n\r\ntype Command = (chain: ChainedCommands) => ChainedCommands\r\n\r\n// Availability is only checked while the menu is open, not on every keystroke\r\nconst groups = computed(() => {\r\n const currentEditor = editor.value\r\n if (!isOpen.value || !currentEditor) return []\r\n\r\n const action = (label: keyof typeof t.value.table, command: Command) => ({\r\n label: t.value.table[label],\r\n enabled: command(currentEditor.can().chain()).run(),\r\n run: () => command(currentEditor.chain().focus()).run(),\r\n })\r\n\r\n if (!isInTable.value) {\r\n return [[action(\"insert\", (c) => c.insertTable(insertOptions.value))]]\r\n }\r\n\r\n return [\r\n [\r\n action(\"addRowBefore\", (c) => c.addRowBefore()),\r\n action(\"addRowAfter\", (c) => c.addRowAfter()),\r\n action(\"deleteRow\", (c) => c.deleteRow()),\r\n ],\r\n [\r\n action(\"addColumnBefore\", (c) => c.addColumnBefore()),\r\n action(\"addColumnAfter\", (c) => c.addColumnAfter()),\r\n action(\"deleteColumn\", (c) => c.deleteColumn()),\r\n ],\r\n [\r\n action(\"toggleHeaderRow\", (c) => c.toggleHeaderRow()),\r\n action(\"mergeOrSplit\", (c) => c.mergeOrSplit()),\r\n ],\r\n [action(\"delete\", (c) => c.deleteTable())],\r\n ]\r\n})\r\n</script>\r\n","// Liquid wrappers around visibility condition blocks in the HTML value:\n//\n// {% if data.approved %}<div class=\"conditionally-visible\" condition=\"data.approved\" …>…</div>{% endif %}\n//\n// The div stays in the output, so the editor can read the block back. On the way\n// in, the wrappers are removed again before the HTML is parsed.\n\nconst CONDITION_SELECTOR = \"div.conditionally-visible\"\n\nexport const liquidIf = (condition: string) => `{% if ${condition} %}`\nexport const LIQUID_ENDIF = \"{% endif %}\"\n\nconst IF_PATTERN = /^\\{% if .* %\\}$/\n\nconst parse = (html: string) => {\n const template = document.createElement(\"template\")\n template.innerHTML = html\n return template\n}\n\n/**\n * Wraps every visibility condition block in `{% if <condition> %}` … `{% endif %}`.\n */\nexport function addLiquidConditions(html: string): string {\n if (!html.includes(\"conditionally-visible\")) return html\n\n const template = parse(html)\n const conditions: string[] = []\n\n template.content.querySelectorAll(CONDITION_SELECTOR).forEach((block) => {\n const condition = block.getAttribute(\"condition\")\n if (!condition) return\n\n // Markers are swapped for the liquid text after serializing, so the\n // condition isn't HTML-escaped (e.g. \"data.count > 5\" keeps its \">\")\n const index = conditions.push(condition) - 1\n block.before(document.createComment(`rt-if:${index}`))\n block.after(document.createComment(\"rt-endif\"))\n })\n\n return template.innerHTML\n .replace(/<!--rt-if:(\\d+)-->/g, (_, index) => liquidIf(conditions[+index]))\n .split(\"<!--rt-endif-->\")\n .join(LIQUID_ENDIF)\n}\n\n/**\n * Removes the liquid wrappers that `addLiquidConditions` put around visibility\n * condition blocks, so the HTML can be loaded into the editor again.\n */\nexport function stripLiquidConditions(html: string): string {\n if (!html.includes(\"{%\")) return html\n\n const template = parse(html)\n\n // Adjacent blocks share one text node (\"{% endif %}{% if … %}\"), so the\n // wrappers are trimmed from the edges of the neighbouring text\n const trim = (node: ChildNode | null, trimText: (text: string) => string) => {\n if (node?.nodeType !== Node.TEXT_NODE) return\n const original = node.textContent ?? \"\"\n const text = trimText(original)\n if (text === original) return\n if (text.trim()) node.textContent = text\n else node.remove()\n }\n\n template.content.querySelectorAll(CONDITION_SELECTOR).forEach((block) => {\n trim(block.previousSibling, (text) => {\n const start = text.lastIndexOf(\"{% if \")\n return start !== -1 && IF_PATTERN.test(text.slice(start).trimEnd())\n ? text.slice(0, start)\n : text\n })\n trim(block.nextSibling, (text) =>\n text.trimStart().startsWith(LIQUID_ENDIF)\n ? text.trimStart().slice(LIQUID_ENDIF.length)\n : text\n )\n })\n\n return template.innerHTML\n}\n","<template>\r\n <DropdownMenu :open=\"isOpen\" @update:open=\"isOpen = $event\" align=\"start\">\r\n <template #trigger=\"{ attrs }\">\r\n <Button\r\n v-bind=\"attrs\"\r\n type=\"button\"\r\n role=\"button\"\r\n :tabindex=\"-1\"\r\n :disabled=\"!canInsert\"\r\n :data-disabled=\"!canInsert\"\r\n :aria-label=\"t.templates.button\"\r\n :tooltip=\"t.templates.button\"\r\n >\r\n <Icon :name=\"FileTextIcon\" class=\"tiptap-button-icon\" />\r\n <Icon :name=\"ChevronDownIcon\" class=\"tiptap-button-dropdown-small\" />\r\n </Button>\r\n </template>\r\n\r\n <div class=\"tiptap-dropdown-menu-group\">\r\n <Button\r\n v-for=\"(template, index) in templates\"\r\n :key=\"index\"\r\n type=\"button\"\r\n role=\"menuitem\"\r\n @click=\"insert(template)\"\r\n >\r\n <span class=\"tiptap-button-text\">{{ template.name }}</span>\r\n </Button>\r\n </div>\r\n </DropdownMenu>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { useTranslations } from \"@/composables/use-translations\"\r\nimport { ref } from \"vue\"\r\nimport { useTiptapEditor } from \"@/composables/use-tiptap-editor\"\r\nimport { useEditorState } from \"@/composables/use-editor-state\"\r\nimport { Button } from \"@/components/tiptap-ui-primitive/button\"\r\nimport { DropdownMenu } from \"@/components/tiptap-ui-primitive/dropdown-menu\"\r\nimport Icon from \"@/components/tiptap-icons/Icon.vue\"\r\nimport { ChevronDownIcon, FileTextIcon } from \"@/components/tiptap-icons\"\r\nimport { stripLiquidConditions } from \"./liquid\"\r\nimport type { RichtextTemplate } from \"./types\"\r\n\r\nconst t = useTranslations()\r\n\r\n/**\r\n * Dropdown with predefined texts (options.templates), inserted at the cursor.\r\n */\r\ndefineProps<{ templates: RichtextTemplate[] }>()\r\n\r\nconst editor = useTiptapEditor()\r\nconst isOpen = ref(false)\r\nconst canInsert = useEditorState(editor, (editor) => !!editor?.isEditable)\r\n\r\nconst escapeHtml = (text: string) =>\r\n text.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\")\r\n\r\n/**\r\n * HTML (e.g. with mention spans) is inserted as-is, like a value; plain text\r\n * becomes one paragraph per line.\r\n */\r\nfunction toContent(text: string) {\r\n if (/<[a-z][\\s\\S]*>/i.test(text)) return stripLiquidConditions(text)\r\n return text\r\n .split(/\\r?\\n/)\r\n .map((line) => `<p>${escapeHtml(line)}</p>`)\r\n .join(\"\")\r\n}\r\n\r\nfunction insert(template: RichtextTemplate) {\r\n editor.value?.chain().focus().insertContent(toContent(template.text)).run()\r\n}\r\n</script>\r\n","<template>\r\n <Portal>\r\n <div\r\n v-if=\"isVisible && media\"\r\n ref=\"toolbarRef\"\r\n popover=\"manual\"\r\n class=\"tiptap-toolbar rt-media-controls\"\r\n data-variant=\"floating\"\r\n role=\"toolbar\"\r\n :aria-label=\"t.media.toolbar\"\r\n :style=\"position\"\r\n @focusin=\"focusInside = true\"\r\n @focusout=\"onFocusOut\"\r\n >\r\n <ToolbarGroup>\r\n <Button\r\n v-for=\"size in SIZES\"\r\n :key=\"size.value\"\r\n type=\"button\"\r\n :title=\"t.media[size.value]\"\r\n :data-active-state=\"media.size === size.value ? 'on' : 'off'\"\r\n :aria-pressed=\"String(media.size === size.value)\"\r\n @mousedown.prevent\r\n @click=\"setAttributes({ size: size.value })\"\r\n >\r\n <span class=\"tiptap-button-text\">{{ size.label }}</span>\r\n </Button>\r\n </ToolbarGroup>\r\n\r\n <ToolbarSeparator />\r\n\r\n <ToolbarGroup>\r\n <Button\r\n v-for=\"float in FLOATS\"\r\n :key=\"float.value\"\r\n type=\"button\"\r\n :title=\"t.media[float.title]\"\r\n :data-active-state=\"media.float === float.value ? 'on' : 'off'\"\r\n :aria-pressed=\"String(media.float === float.value)\"\r\n @mousedown.prevent\r\n @click=\"setAttributes({ float: float.value })\"\r\n >\r\n <Icon :name=\"float.icon\" class=\"tiptap-button-icon\" />\r\n </Button>\r\n </ToolbarGroup>\r\n\r\n <template v-if=\"media.type === 'image'\">\r\n <ToolbarSeparator />\r\n <Input\r\n v-model=\"caption\"\r\n class=\"rt-media-caption\"\r\n :placeholder=\"t.media.caption\"\r\n :aria-label=\"t.media.caption\"\r\n @change=\"setAttributes({ alt: caption })\"\r\n @keydown.enter.prevent=\"setAttributes({ alt: caption })\"\r\n />\r\n </template>\r\n </div>\r\n </Portal>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport Portal from \"@/components/tiptap-ui-primitive/portal/Portal.vue\"\r\nimport { useTranslations } from \"@/composables/use-translations\"\r\nimport { computed, nextTick, onBeforeUnmount, ref, watch } from \"vue\"\r\nimport { useTopLayer } from \"@/composables/use-top-layer\"\r\nimport { NodeSelection } from \"@tiptap/pm/state\"\r\nimport { useTiptapEditor } from \"@/composables/use-tiptap-editor\"\r\nimport { useEditorState } from \"@/composables/use-editor-state\"\r\nimport { Button } from \"@/components/tiptap-ui-primitive/button\"\r\nimport { Input } from \"@/components/tiptap-ui-primitive/input\"\r\nimport { ToolbarGroup, ToolbarSeparator } from \"@/components/tiptap-ui-primitive/toolbar\"\r\nimport Icon from \"@/components/tiptap-icons/Icon.vue\"\r\nimport { AlignCenterIcon, AlignLeftIcon, AlignRightIcon } from \"@/components/tiptap-icons\"\r\n\r\nconst t = useTranslations()\r\n\r\n/**\r\n * Floating toolbar above a selected image or YouTube video: size, float and\r\n * (for images) the caption, stored as the size/float/alt attributes.\r\n */\r\nconst MEDIA_TYPES = [\"image\", \"youtube\"]\r\nconst GAP = 8\r\n\r\nconst SIZES = [\r\n { value: \"small\", label: \"S\" },\r\n { value: \"medium\", label: \"M\" },\r\n { value: \"large\", label: \"L\" },\r\n] as const\r\nconst FLOATS = [\r\n { value: \"left\", icon: AlignLeftIcon, title: \"floatLeft\" },\r\n { value: \"none\", icon: AlignCenterIcon, title: \"floatNone\" },\r\n { value: \"right\", icon: AlignRightIcon, title: \"floatRight\" },\r\n] as const\r\n\r\nconst editor = useTiptapEditor()\r\nconst toolbarRef = ref<HTMLElement | null>(null)\r\nconst focusInside = ref(false)\r\nconst caption = ref(\"\")\r\n\r\nconst media = useEditorState(editor, (editor) => {\r\n const selection = editor?.state.selection\r\n if (\r\n !(selection instanceof NodeSelection) ||\r\n !MEDIA_TYPES.includes(selection.node.type.name)\r\n ) {\r\n return null\r\n }\r\n\r\n const { attrs } = selection.node\r\n return {\r\n type: selection.node.type.name as \"image\" | \"youtube\",\r\n pos: selection.from,\r\n size: (attrs.size as string) ?? \"large\",\r\n float: (attrs.float as string) ?? \"none\",\r\n alt: (attrs.alt as string) ?? \"\",\r\n editorFocused: !!editor?.isFocused,\r\n }\r\n})\r\n\r\n// Stays open while typing a caption, when the editor itself has lost focus\r\nconst isVisible = computed(\r\n () => !!media.value && (media.value.editorFocused || focusInside.value)\r\n)\r\n\r\nfunction onFocusOut(event: FocusEvent) {\r\n if (!toolbarRef.value?.contains(event.relatedTarget as Node | null)) {\r\n focusInside.value = false\r\n }\r\n}\r\n\r\nwatch(\r\n () => media.value && `${media.value.pos}:${media.value.alt}`,\r\n () => (caption.value = media.value?.alt ?? \"\")\r\n)\r\n\r\nfunction setAttributes(attributes: Record<string, string>) {\r\n if (!media.value || !editor.value) return\r\n // Large media are full width, so they can't float\r\n const next = attributes.size === \"large\" ? { ...attributes, float: \"none\" } : attributes\r\n\r\n editor.value\r\n .chain()\r\n .setNodeSelection(media.value.pos)\r\n .updateAttributes(media.value.type, next)\r\n .run()\r\n}\r\n\r\n// --- Position: above the media, or below it when there's no room ---\r\n\r\nconst rect = ref<DOMRect | null>(null)\r\nconst size = ref({ width: 0, height: 0 })\r\n\r\nconst measure = () => {\r\n if (!media.value || !editor.value) return\r\n const dom = editor.value.view.nodeDOM(media.value.pos)\r\n rect.value = dom instanceof HTMLElement ? dom.getBoundingClientRect() : null\r\n size.value = {\r\n width: toolbarRef.value?.offsetWidth ?? 0,\r\n height: toolbarRef.value?.offsetHeight ?? 0,\r\n }\r\n}\r\n\r\nconst position = computed(() => {\r\n if (!rect.value) return { visibility: \"hidden\" as const }\r\n const { width, height } = size.value\r\n const above = rect.value.top - height - GAP\r\n const left = rect.value.left + rect.value.width / 2 - width / 2\r\n\r\n return {\r\n top: `${above >= GAP ? above : rect.value.bottom + GAP}px`,\r\n left: `${Math.max(GAP, Math.min(left, window.innerWidth - width - GAP))}px`,\r\n }\r\n})\r\n\r\n// In the top layer, so it also shows in modals and fullscreen\r\nuseTopLayer(toolbarRef, measure)\r\n\r\nwatch(\r\n () => [isVisible.value, media.value],\r\n async () => {\r\n await nextTick()\r\n measure()\r\n }\r\n)\r\n\r\nconst onViewportChange = () => {\r\n if (isVisible.value) measure()\r\n}\r\nwindow.addEventListener(\"scroll\", onViewportChange, true)\r\nwindow.addEventListener(\"resize\", onViewportChange)\r\nonBeforeUnmount(() => {\r\n window.removeEventListener(\"scroll\", onViewportChange, true)\r\n window.removeEventListener(\"resize\", onViewportChange)\r\n})\r\n</script>\r\n","<template>\n <div class=\"rt-character-count\" :data-state=\"state\" aria-live=\"polite\">\n <span>\n <template v-if=\"limit\">{{ characters }} / {{ limit }}</template>\n <template v-else>{{ t.characterCount.count(characters) }}</template>\n </span>\n <span v-if=\"state === 'below-min'\" class=\"rt-character-count-hint\">\n {{ t.characterCount.atLeast(min ?? 0) }}\n </span>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { useTranslations } from \"@/composables/use-translations\"\nimport { computed } from \"vue\"\nimport { useTiptapEditor } from \"@/composables/use-tiptap-editor\"\nimport { useEditorState } from \"@/composables/use-editor-state\"\n\nconst t = useTranslations()\n\n/**\n * Character count under the editor. `limit` is enforced by Tiptap's\n * CharacterCount extension; `min` only shows a hint.\n */\nconst props = defineProps<{\n min?: number\n limit?: number\n}>()\n\nconst editor = useTiptapEditor()\n\nconst characters = useEditorState(\n editor,\n (editor) => (editor?.storage.characterCount?.characters() as number | undefined) ?? 0\n)\n\nconst state = computed(() => {\n if (props.limit && characters.value >= props.limit) return \"at-limit\"\n if (props.min && characters.value < props.min) return \"below-min\"\n return undefined\n})\n</script>\n","<template>\r\n <nav v-if=\"headings.length >= MIN_HEADINGS\" class=\"rt-toc\" :aria-label=\"t.tableOfContents\">\r\n <!-- One line per heading, shorter for lower levels; the list shows on hover or focus -->\r\n <button type=\"button\" class=\"rt-toc-lines\" :aria-label=\"t.tableOfContents\">\r\n <span\r\n v-for=\"(heading, index) in headings\"\r\n :key=\"index\"\r\n class=\"rt-toc-line\"\r\n :data-rank=\"heading.rank\"\r\n />\r\n </button>\r\n <ol class=\"rt-toc-list\">\r\n <li v-for=\"(heading, index) in headings\" :key=\"index\">\r\n <button\r\n type=\"button\"\r\n class=\"rt-toc-item\"\r\n :data-rank=\"heading.rank\"\r\n :title=\"heading.text\"\r\n @click=\"scrollTo(index)\"\r\n >\r\n {{ heading.text }}\r\n </button>\r\n </li>\r\n </ol>\r\n </nav>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport type { Node as PMNode } from \"@tiptap/pm/model\"\r\nimport { useTiptapEditor } from \"@/composables/use-tiptap-editor\"\r\nimport { useEditorState } from \"@/composables/use-editor-state\"\r\nimport { useTranslations } from \"@/composables/use-translations\"\r\n\r\n/** Fewer headings don't need a table of contents */\r\nconst MIN_HEADINGS = 2\r\n\r\ninterface TocHeading {\r\n text: string\r\n /** 0 for the highest level in the document, 1 for the level below, … */\r\n rank: number\r\n}\r\n\r\nconst t = useTranslations()\r\nconst editor = useTiptapEditor()\r\n\r\n/** Positions of the headings, in order. Only read when one is clicked. */\r\nconst headingPositions = (doc: PMNode) => {\r\n const positions: number[] = []\r\n doc.descendants((node, pos) => {\r\n if (node.type.name === \"heading\" && node.textContent.trim()) positions.push(pos)\r\n return !node.isTextblock\r\n })\r\n return positions\r\n}\r\n\r\nlet lastDoc: PMNode | null = null\r\nlet lastHeadings: TocHeading[] = []\r\n\r\n/**\r\n * Collected again only when the document changed. The list leaves out the\r\n * positions, so typing elsewhere doesn't re-render it.\r\n */\r\nconst headings = useEditorState(\r\n editor,\r\n (editor) => {\r\n const doc = editor?.state.doc\r\n if (!doc) return []\r\n if (doc === lastDoc) return lastHeadings\r\n\r\n const nodes = headingPositions(doc).map((pos) => doc.nodeAt(pos)!)\r\n const top = Math.min(...nodes.map((node) => node.attrs.level as number))\r\n lastDoc = doc\r\n lastHeadings = nodes.map((node) => ({\r\n text: node.textContent.trim(),\r\n rank: Math.min((node.attrs.level as number) - top, 3),\r\n }))\r\n return lastHeadings\r\n },\r\n { equals: (a, b) => JSON.stringify(a) === JSON.stringify(b) }\r\n)\r\n\r\nfunction scrollTo(index: number) {\r\n const view = editor.value?.view\r\n const pos = view && headingPositions(view.state.doc)[index]\r\n if (!view || pos == null) return\r\n const dom = view.nodeDOM(pos)\r\n if (dom instanceof HTMLElement) dom.scrollIntoView({ behavior: \"smooth\", block: \"start\" })\r\n}\r\n</script>\r\n","import { mergeAttributes } from \"@tiptap/core\"\r\nimport { Mention } from \"@tiptap/extension-mention\"\r\nimport { DOMSerializer, type DOMOutputSpec, type Node as PMNode } from \"@tiptap/pm/model\"\r\nimport { Plugin, PluginKey } from \"@tiptap/pm/state\"\r\nimport { Decoration, DecorationSet } from \"@tiptap/pm/view\"\r\nimport type { MentionItem, MentionMenu } from \"./mention-menu\"\r\nimport { PREVIEWS_CHANGED } from \"./previews\"\r\nimport type { MentionGroupOptions, RichtextAction } from \"../types\"\r\n\r\nconst MAX_ITEMS = 10\r\nconst previewsKey = new PluginKey<DecorationSet>(\"mentionPreviews\")\r\n\r\n/**\r\n * Adds a `preview` attribute to every mention whose id has a preview value.\r\n * Decorations only affect the editor view, so the stored value keeps the\r\n * template tag (`data-id=\"{{ data.name }}\"`) and never the preview.\r\n */\r\nfunction previewDecorations(doc: PMNode, previews: Record<string, unknown>) {\r\n if (!Object.keys(previews).length) return DecorationSet.empty\r\n\r\n const decorations: Decoration[] = []\r\n doc.descendants((node, pos) => {\r\n if (node.type.name !== \"mention\") return\r\n const preview = previews[String(node.attrs.id)]\r\n if (preview == null || preview === \"\") return\r\n decorations.push(\r\n Decoration.node(pos, pos + node.nodeSize, { preview: String(preview) })\r\n )\r\n })\r\n return DecorationSet.create(doc, decorations)\r\n}\r\n\r\n/**\r\n * Suggestions matching the query, sorted by name so \"Group: Name\" items end up\r\n * together, and at most MAX_ITEMS.\r\n */\r\nfunction getItems(group: MentionGroupOptions, query: string): MentionItem[] {\r\n const search = query.toLowerCase()\r\n\r\n return group.suggestions\r\n .filter((suggestion) => suggestion.name.toLowerCase().includes(search))\r\n .sort((a, b) => a.name.localeCompare(b.name))\r\n .slice(0, MAX_ITEMS)\r\n .map((suggestion) => {\r\n const [prefix, ...rest] = suggestion.name.split(\":\")\r\n const hasGroup = rest.length > 0\r\n return {\r\n id: suggestion.id,\r\n // \"Women: Paula\" is listed as \"Paula\" under a \"Women\" heading\r\n label: hasGroup ? rest.join(\":\").trim() : suggestion.name,\r\n group: hasGroup ? prefix.trim() : undefined,\r\n suggestion,\r\n }\r\n })\r\n}\r\n\r\nexport interface MentionContext {\r\n groups: Record<string, MentionGroupOptions>\r\n menu: MentionMenu\r\n getPreviews: () => Record<string, unknown>\r\n onAction: (action: RichtextAction) => void\r\n}\r\n\r\n/**\r\n * Mentions with one trigger character per group in `options.mentions`\r\n * (e.g. \"@\" for assignees and \"#\" for labels).\r\n *\r\n * Renders as `<span class=\"mention mention-<group> <class>\" data-type=\"mention\"\r\n * data-id data-label data-mention-suggestion-char>`, readable by v-richtext 2.x.\r\n */\r\nexport function createMention({ groups, menu, getPreviews, onAction }: MentionContext) {\r\n const entries = Object.entries(groups)\r\n const groupsByChar = new Map(\r\n entries.map(([name, group]) => [group.key || \"@\", { name, group }])\r\n )\r\n\r\n /** The key (\"@\", or \"\" with keyInMention: false) and label of a mention */\r\n const mentionText = (node: PMNode) => {\r\n const char: string = node.attrs.mentionSuggestionChar ?? \"@\"\r\n const entry = groupsByChar.get(char)\r\n return {\r\n char: entry?.group.keyInMention === false ? \"\" : char,\r\n label: String(node.attrs.label ?? node.attrs.id ?? \"\"),\r\n }\r\n }\r\n\r\n return Mention.extend({\r\n /**\r\n * The editor shows the key and label as separate spans, so the key can be\r\n * dimmed and a preview can replace both (input-richtext.scss). The stored\r\n * HTML (renderHTML below) is plain text: `<span data-type=\"mention\">@Name</span>`.\r\n */\r\n addNodeView() {\r\n return ({ node }) => {\r\n const toDOM = node.type.spec.toDOM!\r\n const dom = DOMSerializer.renderSpec(document, toDOM(node)).dom as HTMLElement\r\n const { char, label } = mentionText(node)\r\n const part = (className: string, text: string) =>\r\n Object.assign(document.createElement(\"span\"), { className, textContent: text })\r\n\r\n dom.replaceChildren(\r\n ...(char ? [part(\"mention-char\", char)] : []),\r\n part(\"mention-label\", label)\r\n )\r\n return { dom }\r\n }\r\n },\r\n\r\n addProseMirrorPlugins() {\r\n return [\r\n ...(this.parent?.() ?? []),\r\n new Plugin({\r\n key: previewsKey,\r\n state: {\r\n init: (_, state) => previewDecorations(state.doc, getPreviews()),\r\n apply: (tr, decorations) =>\r\n tr.docChanged || tr.getMeta(PREVIEWS_CHANGED)\r\n ? previewDecorations(tr.doc, getPreviews())\r\n : decorations,\r\n },\r\n props: {\r\n decorations: (state) => previewsKey.getState(state),\r\n },\r\n }),\r\n ]\r\n },\r\n }).configure({\r\n HTMLAttributes: { class: \"mention\" },\r\n\r\n suggestions: entries.map(([name, group]) => {\r\n const char = group.key || \"@\"\r\n\r\n return {\r\n char,\r\n pluginKey: new PluginKey(`mention-${name}`),\r\n items: ({ query }: { query: string }) => getItems(group, query),\r\n render: menu.render,\r\n command: ({ editor, range, props }) => {\r\n const { suggestion, label, group: groupName } = props as unknown as MentionItem\r\n\r\n if (suggestion.hooks || group.hooks) {\r\n onAction({\r\n action: \"select_mention\",\r\n name,\r\n options: group,\r\n suggestion,\r\n hooks: [...(suggestion.hooks ?? []), ...(group.hooks ?? [])],\r\n })\r\n }\r\n\r\n // Not applied: only the action fires, the typed \"@query\" is removed\r\n if (suggestion.apply === false || group.apply === false) {\r\n editor.chain().focus().deleteRange(range).run()\r\n return\r\n }\r\n\r\n // Swallow an existing space after the cursor, a new one is added below\r\n const nodeAfter = editor.view.state.selection.$to.nodeAfter\r\n const to = nodeAfter?.text?.startsWith(\" \") ? range.to + 1 : range.to\r\n\r\n editor\r\n .chain()\r\n .focus()\r\n .insertContentAt({ from: range.from, to }, [\r\n {\r\n type: \"mention\",\r\n attrs: {\r\n id: suggestion.id,\r\n // Stored with its group, e.g. \"Women: Paula\"\r\n label: groupName ? `${groupName}: ${label}` : label,\r\n mentionSuggestionChar: char,\r\n },\r\n },\r\n { type: \"text\", text: \" \" },\r\n ])\r\n .run()\r\n\r\n editor.view.dom.ownerDocument.defaultView\r\n ?.getSelection()\r\n ?.collapseToEnd()\r\n },\r\n }\r\n }),\r\n\r\n renderHTML({ options, node }): DOMOutputSpec {\r\n const entry = groupsByChar.get(node.attrs.mentionSuggestionChar ?? \"@\")\r\n const groupClass = entry\r\n ? [`mention-${entry.name}`, entry.group.class].filter(Boolean).join(\" \")\r\n : undefined\r\n const { char, label } = mentionText(node)\r\n\r\n return [\r\n \"span\",\r\n mergeAttributes(options.HTMLAttributes, groupClass ? { class: groupClass } : {}),\r\n `${char}${label}`,\r\n ]\r\n },\r\n\r\n renderText({ node }) {\r\n return `${node.attrs.mentionSuggestionChar ?? \"@\"}${node.attrs.label ?? node.attrs.id}`\r\n },\r\n })\r\n}\r\n","import type { NodeConfig } from \"@tiptap/core\"\r\n\r\n/** Size / float of an image from the older `custom-image-small custom-image-float-left` classes */\r\nfunction imageClass(element: HTMLElement, attribute: \"size\" | \"float\") {\r\n const pattern =\r\n attribute === \"size\"\r\n ? /(?:^|\\s)custom-image-(small|medium|large)(?:\\s|$)/\r\n : /(?:^|\\s)custom-image-float-(left|right|none)(?:\\s|$)/\r\n return element.className.match(pattern)?.[1] ?? null\r\n}\r\n\r\n/**\r\n * Adds the size/float/loading attributes that p5 content stores on images and\r\n * YouTube embeds, so they survive a round trip through the editor. Size and\r\n * float are also read from the classes of Blixem's older editor\r\n * (`custom-image-small custom-image-float-left`), and written as attributes.\r\n */\r\nexport const mediaAttributes: Partial<NodeConfig> = {\r\n addAttributes() {\r\n return {\r\n ...this.parent?.(),\r\n loading: { default: \"lazy\" },\r\n size: {\r\n default: \"large\",\r\n parseHTML: (element: HTMLElement) =>\r\n element.getAttribute(\"size\") ?? imageClass(element, \"size\") ?? \"large\",\r\n },\r\n float: {\r\n default: \"none\",\r\n parseHTML: (element: HTMLElement) =>\r\n element.getAttribute(\"float\") ?? imageClass(element, \"float\") ?? \"none\",\r\n },\r\n }\r\n },\r\n}\r\n","import { Extension, type JSONContent } from \"@tiptap/core\"\r\nimport type { EditorState } from \"@tiptap/pm/state\"\r\nimport { Plugin, PluginKey } from \"@tiptap/pm/state\"\r\nimport { Decoration, DecorationSet } from \"@tiptap/pm/view\"\r\nimport { DEFAULT_LOCALE, locales, type RichtextMessages } from \"@/locales\"\r\n\r\nexport interface FileDropOptions {\r\n /** Uploads the file and resolves to its URL */\r\n upload: (file: File, type: \"file\" | \"image\") => Promise<string>\r\n /** Insert image files as images (requires the image extension) */\r\n images: boolean\r\n /** Insert other files as file links (requires the file extension) */\r\n files: boolean\r\n /** Texts in the editor's locale */\r\n messages: () => RichtextMessages\r\n}\r\n\r\ntype PlaceholderAction =\r\n | { add: { id: string; pos: number; name: string } }\r\n | { fail: { id: string; message: string } }\r\n | { remove: string }\r\n\r\nconst placeholderKey = new PluginKey<DecorationSet>(\"fileDropPlaceholders\")\r\nlet uid = 0\r\n\r\n/** Where a dropped/pasted block goes: next to the paragraph at `pos`, not inside it */\r\nfunction blockPosition(state: EditorState, pos: number) {\r\n const $pos = state.doc.resolve(Math.min(pos, state.doc.content.size))\r\n if (!$pos.parent.isTextblock) return $pos.pos\r\n return $pos.parentOffset === 0 ? $pos.before() : $pos.after()\r\n}\r\n\r\nfunction placeholderDom(\r\n t: RichtextMessages,\r\n name: string,\r\n error?: string,\r\n onDismiss?: () => void\r\n) {\r\n const dom = document.createElement(\"div\")\r\n dom.className = \"rt-upload-placeholder\"\r\n dom.contentEditable = \"false\"\r\n if (error) dom.dataset.state = \"error\"\r\n\r\n const text = document.createElement(\"span\")\r\n text.textContent = error ? `${name}: ${error}` : t.upload.uploadingFile(name)\r\n dom.append(text)\r\n\r\n if (onDismiss) {\r\n const close = document.createElement(\"button\")\r\n close.type = \"button\"\r\n close.className = \"rt-upload-placeholder-close\"\r\n close.title = t.upload.dismiss\r\n close.textContent = \"×\"\r\n close.addEventListener(\"mousedown\", (event) => event.preventDefault())\r\n close.addEventListener(\"click\", onDismiss)\r\n dom.append(close)\r\n }\r\n return dom\r\n}\r\n\r\n/**\r\n * Uploads files that are dropped on or pasted into the editor. Images become\r\n * images, other files become file links, placed next to the paragraph they\r\n * were dropped on.\r\n *\r\n * While uploading, a placeholder shows where the file will go. It's a widget\r\n * decoration, so it never ends up in the value or the undo history. A failed\r\n * upload leaves the placeholder with the error until it's dismissed.\r\n */\r\nexport const FileDrop = Extension.create<FileDropOptions>({\r\n name: \"fileDrop\",\r\n\r\n addOptions() {\r\n return {\r\n upload: () => Promise.reject(new Error(\"No upload function\")),\r\n images: false,\r\n files: false,\r\n messages: () => locales[DEFAULT_LOCALE],\r\n }\r\n },\r\n\r\n addProseMirrorPlugins() {\r\n const { editor, options } = this\r\n\r\n const dispatch = (action: PlaceholderAction) =>\r\n editor.view.dispatch(editor.state.tr.setMeta(placeholderKey, action))\r\n\r\n const findPlaceholder = (id: string) =>\r\n placeholderKey\r\n .getState(editor.state)\r\n ?.find(undefined, undefined, (spec) => spec.id === id)[0]\r\n\r\n const typeOf = (file: File) =>\r\n options.images && file.type.startsWith(\"image/\")\r\n ? \"image\"\r\n : options.files\r\n ? \"file\"\r\n : null\r\n\r\n const accepted = (files: File[]) => files.filter((file) => typeOf(file))\r\n\r\n const uploadFile = async (file: File, pos: number) => {\r\n const type = typeOf(file)!\r\n const id = `upload-${++uid}`\r\n dispatch({ add: { id, pos, name: file.name } })\r\n\r\n try {\r\n const url = await options.upload(file, type)\r\n const placeholder = findPlaceholder(id)\r\n dispatch({ remove: id })\r\n // Removed (e.g. its paragraph was deleted) while uploading\r\n if (!placeholder) return\r\n\r\n const content: JSONContent =\r\n type === \"image\"\r\n ? { type: \"image\", attrs: { src: url, alt: file.name.replace(/\\.[^/.]+$/, \"\") } }\r\n : { type: \"file\", attrs: { href: url }, content: [{ type: \"text\", text: file.name }] }\r\n editor.chain().insertContentAt(placeholder.from, content).run()\r\n } catch (error) {\r\n dispatch({\r\n fail: { id, message: error instanceof Error ? error.message : options.messages().upload.failed },\r\n })\r\n }\r\n }\r\n\r\n // Uploads run in parallel; each file goes where its placeholder is\r\n const uploadFiles = (files: File[], pos: number) => {\r\n const at = blockPosition(editor.state, pos)\r\n files.forEach((file) => uploadFile(file, at))\r\n }\r\n\r\n return [\r\n new Plugin({\r\n key: placeholderKey,\r\n\r\n state: {\r\n init: () => DecorationSet.empty,\r\n apply: (tr, placeholders) => {\r\n let set = placeholders.map(tr.mapping, tr.doc)\r\n const action = tr.getMeta(placeholderKey) as PlaceholderAction | undefined\r\n if (!action) return set\r\n\r\n if (\"add\" in action) {\r\n const { id, pos, name } = action.add\r\n // side: 1 keeps later placeholders after files inserted at the same spot\r\n set = set.add(tr.doc, [\r\n Decoration.widget(pos, () => placeholderDom(options.messages(), name), { id, name, side: 1 }),\r\n ])\r\n } else if (\"fail\" in action) {\r\n const { id, message } = action.fail\r\n const [placeholder] = set.find(undefined, undefined, (spec) => spec.id === id)\r\n if (!placeholder) return set\r\n set = set.remove([placeholder]).add(tr.doc, [\r\n Decoration.widget(\r\n placeholder.from,\r\n () => placeholderDom(options.messages(), placeholder.spec.name, message, () => dispatch({ remove: id })),\r\n { id, name: placeholder.spec.name, side: 1 }\r\n ),\r\n ])\r\n } else {\r\n const id = action.remove\r\n set = set.remove(set.find(undefined, undefined, (spec) => spec.id === id))\r\n }\r\n return set\r\n },\r\n },\r\n\r\n props: {\r\n decorations: (state) => placeholderKey.getState(state),\r\n\r\n handleDrop: (view, event, _slice, moved) => {\r\n if (moved) return false\r\n const files = accepted(Array.from(event.dataTransfer?.files ?? []))\r\n if (!files.length) return false\r\n\r\n event.preventDefault()\r\n const pos =\r\n view.posAtCoords({ left: event.clientX, top: event.clientY })?.pos ??\r\n view.state.selection.from\r\n uploadFiles(files, pos)\r\n return true\r\n },\r\n\r\n handlePaste: (view, event) => {\r\n const data = event.clipboardData\r\n const files = accepted(Array.from(data?.files ?? []))\r\n // Text or HTML wins, e.g. Word puts a picture of the copied text on the clipboard too\r\n if (\r\n !files.length ||\r\n data?.types.includes(\"text/html\") ||\r\n data?.types.includes(\"text/plain\")\r\n ) {\r\n return false\r\n }\r\n\r\n uploadFiles(files, view.state.selection.from)\r\n return true\r\n },\r\n },\r\n }),\r\n ]\r\n },\r\n})\r\n","import { Link as TiptapLink } from \"@tiptap/extension-link\"\n\n/**\n * Tiptap's link, with a `target` that survives a round trip. Tiptap reads a\n * link without `target` back as its default `_blank`, so a link set to open in\n * the same tab would come back opening in a new one. Here links keep\n * `target=\"_blank\"` as the default, same-tab links store `target=\"_self\"`, and\n * links without a target (from pasted or old content) open in the same tab.\n */\nexport const Link = TiptapLink.extend({\n addAttributes() {\n return {\n ...this.parent?.(),\n target: {\n default: \"_blank\",\n parseHTML: (element) => element.getAttribute(\"target\") || \"_self\",\n },\n }\n },\n})\n","import { TaskItem as TiptapTaskItem } from \"@tiptap/extension-list\"\r\nimport type { RichtextMessages } from \"@/locales\"\r\n\r\n/**\r\n * Tiptap's task item, whose checkbox can also be toggled while the editor is\r\n * read-only (`readonly=\"tasks\"`). Tiptap's `onReadOnlyChecked` only keeps the\r\n * checkbox checked and gets the node as it was when the view was created, so\r\n * the change is written to the document here, which emits the new value.\r\n */\r\nexport function createTaskItem(\r\n canCheckReadonly: () => boolean,\r\n messages: () => RichtextMessages\r\n) {\r\n return TiptapTaskItem.extend({\r\n addNodeView() {\r\n const createView = this.parent?.()\r\n if (!createView) return null\r\n\r\n return (props) => {\r\n const view = createView(props)\r\n const checkbox = (view.dom as HTMLElement).querySelector<HTMLInputElement>(\r\n \":scope > label > input[type='checkbox']\"\r\n )\r\n\r\n // Runs after Tiptap's own listener, which handles the editable case\r\n checkbox?.addEventListener(\"change\", () => {\r\n const { editor, getPos } = props\r\n if (editor.isEditable || !canCheckReadonly()) return\r\n\r\n const pos = typeof getPos === \"function\" ? getPos() : undefined\r\n const node = typeof pos === \"number\" ? editor.state.doc.nodeAt(pos) : null\r\n if (typeof pos !== \"number\" || node?.type.name !== this.name) return\r\n\r\n editor.view.dispatch(\r\n editor.state.tr.setNodeMarkup(pos, undefined, {\r\n ...node.attrs,\r\n checked: checkbox.checked,\r\n })\r\n )\r\n })\r\n\r\n return view\r\n }\r\n },\r\n }).configure({\r\n nested: true,\r\n // Without this, Tiptap unchecks the box again while read-only\r\n onReadOnlyChecked: () => canCheckReadonly(),\r\n a11y: { checkboxLabel: (node) => messages().list.taskCheckbox(node.textContent) },\r\n })\r\n}\r\n","import { Extension } from \"@tiptap/core\"\r\n\r\ndeclare module \"@tiptap/core\" {\r\n interface Commands<ReturnType> {\r\n headingId: {\r\n /** Sets the `id` (anchor) of the heading at the cursor; empty removes it */\r\n setHeadingId: (id: string | null) => ReturnType\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * `id` on headings, e.g. `<h2 id=\"contact\">`, so pages can link to them\r\n * (`#contact`). Kept when content is loaded and saved.\r\n */\r\nexport const HeadingId = Extension.create({\r\n name: \"headingId\",\r\n\r\n addGlobalAttributes() {\r\n return [\r\n {\r\n types: [\"heading\"],\r\n attributes: {\r\n id: {\r\n default: null,\r\n parseHTML: (element) => element.getAttribute(\"id\") || null,\r\n renderHTML: (attributes) => (attributes.id ? { id: attributes.id } : {}),\r\n },\r\n },\r\n },\r\n ]\r\n },\r\n\r\n addCommands() {\r\n return {\r\n setHeadingId:\r\n (id) =>\r\n ({ commands }) =>\r\n commands.updateAttributes(\"heading\", { id: id?.trim() || null }),\r\n }\r\n },\r\n})\r\n","import type { Extensions } from \"@tiptap/core\"\r\nimport { StarterKit } from \"@tiptap/starter-kit\"\r\nimport { Image } from \"@tiptap/extension-image\"\r\nimport { TaskList } from \"@tiptap/extension-list\"\r\nimport { TextAlign } from \"@tiptap/extension-text-align\"\r\nimport { Typography } from \"@tiptap/extension-typography\"\r\nimport { Highlight } from \"@tiptap/extension-highlight\"\r\nimport { Subscript } from \"@tiptap/extension-subscript\"\r\nimport { Superscript } from \"@tiptap/extension-superscript\"\r\nimport { CharacterCount, Placeholder, Selection } from \"@tiptap/extensions\"\r\nimport Youtube from \"@tiptap/extension-youtube\"\r\nimport { TableKit } from \"@tiptap/extension-table\"\r\n\r\n\r\nimport { createMention } from \"./mention\"\r\nimport type { MentionMenu } from \"./mention-menu\"\r\nimport { mediaAttributes } from \"./media-attributes\"\r\nimport { FileNode } from \"./file-node\"\r\nimport { FileDrop } from \"./file-drop\"\r\nimport { Link } from \"./link\"\r\nimport { createTaskItem } from \"./task-item\"\r\nimport { VisibilityConditions } from \"./visibility-conditions\"\r\nimport { HeadingId } from \"./heading-id\"\r\nimport type { RichtextMessages } from \"@/locales\"\r\nimport type {\r\n HeadingLevel,\r\n RichtextAction,\r\n RichtextExtensionName,\r\n RichtextOptions,\r\n} from \"../types\"\r\n\r\n/** Enabled without any options; `options.enabled` / `options.disabled` change this set */\r\nexport const DEFAULT_EXTENSIONS: RichtextExtensionName[] = [\r\n \"bold\",\r\n \"italic\",\r\n \"strike\",\r\n \"underline\",\r\n \"link\",\r\n \"heading\",\r\n \"bulletList\",\r\n \"orderedList\",\r\n \"placeholder\",\r\n \"typography\",\r\n \"mediaControls\",\r\n \"tableOfContents\",\r\n]\r\n\r\n/**\r\n * YouTube iframes always get width=\"640\" height=\"360\": a 16:9 ratio, also for\r\n * videos stored with other numbers. The actual size comes from CSS.\r\n */\r\nconst youtubeRatio = {\r\n addAttributes(this: { parent?: () => Record<string, unknown> }) {\r\n return {\r\n ...this.parent?.(),\r\n width: { default: 640, parseHTML: () => 640 },\r\n height: { default: 360, parseHTML: () => 360 },\r\n }\r\n },\r\n}\r\n\r\nexport const DEFAULT_HEADING_LEVELS: HeadingLevel[] = [2, 3]\r\n\r\n/**\r\n * Buttons that `options.hidden` can remove from the toolbar: their function\r\n * stays available through Tiptap's shortcuts and input rules. Popover and menu\r\n * buttons (link, image, table, …) aren't in here, because their shortcuts\r\n * work by clicking the button. visibilityConditions is the exception: hiding\r\n * its menu keeps existing condition blocks (and their previews) working, but\r\n * no new ones can be added.\r\n */\r\nexport const HIDEABLE_BUTTONS = new Set<RichtextExtensionName>([\r\n \"bold\",\r\n \"italic\",\r\n \"strike\",\r\n \"underline\",\r\n \"code\",\r\n \"superscript\",\r\n \"subscript\",\r\n \"blockquote\",\r\n \"codeBlock\",\r\n \"textAlign\",\r\n \"visibilityConditions\",\r\n])\r\n\r\nconst SUPPORTED_EXTENSIONS = new Set<string>([\r\n ...DEFAULT_EXTENSIONS,\r\n \"taskList\",\r\n \"undoRedo\",\r\n \"code\",\r\n \"highlight\",\r\n \"subscript\",\r\n \"superscript\",\r\n \"blockquote\",\r\n \"codeBlock\",\r\n \"horizontalRule\",\r\n \"textAlign\",\r\n \"image\",\r\n \"file\",\r\n \"youtube\",\r\n \"table\",\r\n \"characterCount\",\r\n \"templates\",\r\n \"mention\",\r\n \"visibilityConditions\",\r\n] satisfies RichtextExtensionName[])\r\n\r\n/** Enabled extensions whose toolbar button is hidden (`options.hidden`) */\r\nexport function resolveHiddenButtons(\r\n options: RichtextOptions = {},\r\n enabled: Set<RichtextExtensionName>\r\n): Set<RichtextExtensionName> {\r\n const hidden = new Set<RichtextExtensionName>()\r\n const unsupported: string[] = []\r\n\r\n options.hidden?.forEach((name) => {\r\n if (HIDEABLE_BUTTONS.has(name as RichtextExtensionName)) {\r\n hidden.add(name as RichtextExtensionName)\r\n } else {\r\n unsupported.push(name)\r\n }\r\n })\r\n\r\n if (unsupported.length) {\r\n console.warn(\r\n `[richtext] These buttons can't be hidden and stay visible: ${unsupported.join(\", \")}`\r\n )\r\n }\r\n return new Set([...hidden].filter((name) => enabled.has(name)))\r\n}\r\n\r\n/**\r\n * The extensions to load: the defaults, plus `enabled`, plus character count,\r\n * mentions and visibility conditions when their options are set, minus `disabled`.\r\n */\r\nexport function resolveExtensions(\r\n options: RichtextOptions = {}\r\n): Set<RichtextExtensionName> {\r\n const names = new Set<string>(DEFAULT_EXTENSIONS)\r\n options.enabled?.forEach((name) => names.add(name))\r\n if (options.characterCount) names.add(\"characterCount\")\r\n if (options.templates?.length) names.add(\"templates\")\r\n if (Object.keys(options.mentions ?? {}).length) names.add(\"mention\")\r\n if (options.visibilityConditions?.conditions?.length) {\r\n names.add(\"visibilityConditions\")\r\n }\r\n options.disabled?.forEach((name) => names.delete(name))\r\n\r\n const unsupported = [...names].filter((name) => !SUPPORTED_EXTENSIONS.has(name))\r\n if (unsupported.length) {\r\n console.warn(\r\n `[richtext] Unsupported extensions are ignored: ${unsupported.join(\", \")}`\r\n )\r\n }\r\n\r\n return new Set(\r\n [...names].filter((name) => SUPPORTED_EXTENSIONS.has(name))\r\n ) as Set<RichtextExtensionName>\r\n}\r\n\r\nexport interface ExtensionContext {\r\n placeholder: () => string\r\n mentionMenu: MentionMenu | null\r\n getMentionPreviews: () => Record<string, unknown>\r\n onAction: (action: RichtextAction) => void\r\n /** For dropped and pasted files */\r\n uploadFile: (file: File, type: \"file\" | \"image\") => Promise<string>\r\n /** Whether task checkboxes can be toggled while the editor is read-only */\r\n canCheckTasksReadonly: () => boolean\r\n /** Texts in the editor's locale */\r\n messages: () => RichtextMessages\r\n}\r\n\r\nexport function buildExtensions(\r\n enabled: Set<RichtextExtensionName>,\r\n options: RichtextOptions,\r\n context: ExtensionContext\r\n): Extensions {\r\n const has = (name: RichtextExtensionName) => enabled.has(name)\r\n // StarterKit takes `false` to leave an extension out\r\n const toggle = <T extends object>(name: RichtextExtensionName, config?: T) =>\r\n has(name) ? (config ?? {}) : false\r\n const hasBasicList = has(\"bulletList\") || has(\"orderedList\")\r\n\r\n const extensions: Extensions = [\r\n StarterKit.configure({\r\n bold: toggle(\"bold\"),\r\n italic: toggle(\"italic\"),\r\n strike: toggle(\"strike\"),\r\n underline: toggle(\"underline\"),\r\n code: toggle(\"code\"),\r\n blockquote: toggle(\"blockquote\"),\r\n codeBlock: toggle(\"codeBlock\"),\r\n heading: toggle(\"heading\", {\r\n levels: options.heading?.levels ?? DEFAULT_HEADING_LEVELS,\r\n }),\r\n bulletList: toggle(\"bulletList\"),\r\n orderedList: toggle(\"orderedList\"),\r\n listItem: hasBasicList ? {} : false,\r\n listKeymap: hasBasicList ? {} : false,\r\n // Added below, with a target that survives a round trip\r\n link: false,\r\n // p5: StarterKit's <hr> instead of the template's <div><hr></div> wrapper\r\n horizontalRule: toggle(\"horizontalRule\"),\r\n }),\r\n Selection,\r\n HeadingId,\r\n ]\r\n\r\n if (has(\"link\")) {\r\n extensions.push(Link.configure({ openOnClick: false, enableClickSelection: true }))\r\n }\r\n if (has(\"textAlign\")) {\r\n extensions.push(TextAlign.configure({ types: [\"heading\", \"paragraph\"] }))\r\n }\r\n if (has(\"taskList\")) {\r\n extensions.push(TaskList, createTaskItem(context.canCheckTasksReadonly, context.messages))\r\n }\r\n if (has(\"highlight\")) extensions.push(Highlight.configure({ multicolor: true }))\r\n if (has(\"subscript\")) extensions.push(Subscript)\r\n if (has(\"superscript\")) extensions.push(Superscript)\r\n if (has(\"typography\")) extensions.push(Typography)\r\n if (has(\"placeholder\")) {\r\n extensions.push(Placeholder.configure({ placeholder: context.placeholder }))\r\n }\r\n\r\n // Images are added through the image popover (UploadPopover.vue) or by drop/paste\r\n if (has(\"image\")) extensions.push(Image.extend(mediaAttributes))\r\n if (has(\"file\")) extensions.push(FileNode)\r\n if (has(\"image\") || has(\"file\")) {\r\n extensions.push(\r\n FileDrop.configure({\r\n upload: context.uploadFile,\r\n images: has(\"image\"),\r\n files: has(\"file\"),\r\n messages: context.messages,\r\n })\r\n )\r\n }\r\n if (has(\"table\")) extensions.push(TableKit.configure({ table: { resizable: false } }))\r\n if (has(\"characterCount\")) {\r\n extensions.push(CharacterCount.configure({ limit: options.characterCount?.limit }))\r\n }\r\n if (has(\"youtube\")) {\r\n extensions.push(\r\n Youtube.extend(mediaAttributes).extend(youtubeRatio).configure({\r\n nocookie: true,\r\n modestBranding: true,\r\n ivLoadPolicy: 3,\r\n width: 640,\r\n height: 360,\r\n })\r\n )\r\n }\r\n\r\n if (has(\"mention\") && options.mentions && context.mentionMenu) {\r\n extensions.push(\r\n createMention({\r\n groups: options.mentions,\r\n menu: context.mentionMenu,\r\n getPreviews: context.getMentionPreviews,\r\n onAction: context.onAction,\r\n })\r\n )\r\n }\r\n if (has(\"visibilityConditions\")) {\r\n extensions.push(\r\n VisibilityConditions.configure({\r\n getPreviews: context.getMentionPreviews,\r\n messages: context.messages,\r\n })\r\n )\r\n }\r\n\r\n return extensions\r\n}\r\n","import { reactive } from \"vue\"\nimport type {\n SuggestionKeyDownProps,\n SuggestionOptions,\n SuggestionProps,\n} from \"@tiptap/suggestion\"\nimport type { MentionNodeAttrs } from \"@tiptap/extension-mention\"\nimport type { MentionSuggestion } from \"../types\"\n\nexport interface MentionItem {\n id: string | number\n /** Name without the \"Group:\" prefix; listed and stored as the mention's label */\n label: string\n /** Heading the item is listed under */\n group?: string\n suggestion: MentionSuggestion\n}\n\nexport interface MentionMenuState {\n open: boolean\n items: MentionItem[]\n selectedIndex: number\n /** Position of the typed \"@query\", for placing the menu */\n rect: DOMRect | null\n}\n\nexport type MentionMenu = ReturnType<typeof createMentionMenu>\n\n/**\n * State for the mention suggestion list, shared by all trigger characters of one\n * editor (only one suggestion can be active at a time). The suggestion plugin\n * drives it through `render`, and MentionMenu.vue renders it.\n */\nexport function createMentionMenu() {\n const state = reactive<MentionMenuState>({\n open: false,\n items: [],\n selectedIndex: 0,\n rect: null,\n })\n\n let command: ((item: MentionItem) => void) | null = null\n\n const sync = (props: SuggestionProps<MentionItem, MentionNodeAttrs>) => {\n state.items = props.items\n state.rect = props.clientRect?.() ?? null\n state.selectedIndex = 0\n // The mention's command receives the chosen item, see ./mention.ts\n command = props.command as unknown as (item: MentionItem) => void\n }\n\n const select = (index = state.selectedIndex) => {\n const item = state.items[index]\n if (item) command?.(item)\n }\n\n const onKeyDown = ({ event }: SuggestionKeyDownProps) => {\n if (!state.open || !state.items.length) return false\n const count = state.items.length\n\n switch (event.key) {\n case \"ArrowDown\":\n state.selectedIndex = (state.selectedIndex + 1) % count\n return true\n case \"ArrowUp\":\n state.selectedIndex = (state.selectedIndex - 1 + count) % count\n return true\n case \"Enter\":\n case \"Tab\":\n select()\n return true\n default:\n return false\n }\n }\n\n const render: NonNullable<\n SuggestionOptions<MentionItem, MentionNodeAttrs>[\"render\"]\n > = () => ({\n onStart: (props) => {\n sync(props)\n state.open = true\n },\n onUpdate: sync,\n onKeyDown,\n onExit: () => {\n state.open = false\n command = null\n },\n })\n\n return { state, select, render }\n}\n","<template>\r\n <div\r\n ref=\"rootRef\"\r\n class=\"rt-editor\"\r\n :data-readonly=\"readonly || undefined\"\r\n :data-toolbar=\"toolbarPosition\"\r\n :data-compact=\"compact || undefined\"\r\n :data-invalid=\"showInvalid || undefined\"\r\n @keydown=\"onKeydown\"\r\n >\r\n <Toolbar v-if=\"editor && !readonly\" class=\"rt-editor-toolbar\">\r\n <template v-if=\"mobileView === 'main'\">\r\n <!-- Empty without undoRedo: the toolbar CSS then hides the group and its separator -->\r\n <ToolbarGroup>\r\n <template v-if=\"has('undoRedo')\">\r\n <CommandButton :command=\"undoRedoCommand('undo')\" />\r\n <CommandButton :command=\"undoRedoCommand('redo')\" />\r\n </template>\r\n </ToolbarGroup>\r\n\r\n <template v-if=\"groups.blocks\">\r\n <ToolbarSeparator />\r\n <ToolbarGroup>\r\n <HeadingDropdownMenu v-if=\"has('heading')\" :levels=\"headingLevels\" />\r\n <ListDropdownMenu v-if=\"listTypes.length\" :types=\"listTypes\" />\r\n <CommandButton v-if=\"showButton('blockquote')\" :command=\"blockquoteCommand()\" />\r\n <CommandButton v-if=\"showButton('codeBlock')\" :command=\"codeBlockCommand()\" />\r\n <VisibilityConditionsMenu\r\n v-if=\"showButton('visibilityConditions')\"\r\n :conditions=\"options.visibilityConditions?.conditions ?? []\"\r\n />\r\n </ToolbarGroup>\r\n </template>\r\n\r\n <template v-if=\"groups.marks\">\r\n <ToolbarSeparator />\r\n <ToolbarGroup>\r\n <CommandButton\r\n v-for=\"mark in formattingMarks\"\r\n :key=\"mark\"\r\n :command=\"markCommand(mark)\"\r\n />\r\n <template v-if=\"has('highlight')\">\r\n <ColorHighlightPopover v-if=\"!isMobile\" />\r\n <ColorHighlightPopoverButton\r\n v-else\r\n :data-rt-shortcut=\"SHORTCUTS.highlight\"\r\n @click=\"mobileView = 'highlighter'\"\r\n />\r\n </template>\r\n <template v-if=\"has('link')\">\r\n <LinkPopover v-if=\"!isMobile\" />\r\n <LinkButton\r\n v-else\r\n :data-rt-shortcut=\"SHORTCUTS.link\"\r\n @click=\"mobileView = 'link'\"\r\n />\r\n </template>\r\n </ToolbarGroup>\r\n </template>\r\n\r\n <template v-if=\"groups.scripts\">\r\n <ToolbarSeparator />\r\n <ToolbarGroup>\r\n <CommandButton v-if=\"showButton('superscript')\" :command=\"markCommand('superscript')\" />\r\n <CommandButton v-if=\"showButton('subscript')\" :command=\"markCommand('subscript')\" />\r\n </ToolbarGroup>\r\n </template>\r\n\r\n <template v-if=\"groups.align\">\r\n <ToolbarSeparator />\r\n <ToolbarGroup>\r\n <CommandButton\r\n v-for=\"align in textAligns\"\r\n :key=\"align\"\r\n :command=\"textAlignCommand(align)\"\r\n />\r\n </ToolbarGroup>\r\n </template>\r\n\r\n <template v-if=\"groups.media\">\r\n <ToolbarSeparator />\r\n <ToolbarGroup>\r\n <UploadPopover\r\n v-if=\"has('image')\"\r\n type=\"image\"\r\n :upload=\"onUploadFile ? uploadFile : undefined\"\r\n :shortcut-keys=\"SHORTCUTS.image\"\r\n />\r\n <UploadPopover\r\n v-if=\"has('file')\"\r\n type=\"file\"\r\n :upload=\"onUploadFile ? uploadFile : undefined\"\r\n :shortcut-keys=\"SHORTCUTS.file\"\r\n />\r\n <YoutubePopover v-if=\"has('youtube')\" />\r\n <TableMenu\r\n v-if=\"has('table')\"\r\n :insert-defaults=\"options.table?.insertDefaults\"\r\n />\r\n <TemplatesMenu v-if=\"has('templates')\" :templates=\"options.templates ?? []\" />\r\n </ToolbarGroup>\r\n </template>\r\n </template>\r\n\r\n <template v-else>\r\n <ToolbarGroup>\r\n <Button :aria-label=\"t.back\" :tooltip=\"t.back\" @click=\"mobileView = 'main'\">\r\n <Icon :name=\"ArrowLeftIcon\" class=\"tiptap-button-icon\" />\r\n <Icon\r\n :name=\"mobileView === 'highlighter' ? HighlighterIcon : LinkIcon\"\r\n class=\"tiptap-button-icon\"\r\n />\r\n </Button>\r\n </ToolbarGroup>\r\n <ToolbarSeparator />\r\n <ColorHighlightPopoverContent v-if=\"mobileView === 'highlighter'\" />\r\n <LinkContent v-else />\r\n </template>\r\n\r\n <Spacer />\r\n <slot name=\"after-menu\" />\r\n </Toolbar>\r\n\r\n <TableOfContents v-if=\"editor && has('tableOfContents')\" />\r\n\r\n <EditorContent :editor=\"editor\" class=\"rt-editor-content\" />\r\n\r\n <!-- Takes part in native form validation (required, characterCount.min) -->\r\n <textarea\r\n v-if=\"isFormField\"\r\n ref=\"formField\"\r\n class=\"rt-form-field\"\r\n :required=\"required\"\r\n :value=\"validity.empty ? '' : ' '\"\r\n tabindex=\"-1\"\r\n aria-hidden=\"true\"\r\n @keydown=\"onFormFieldKeydown\"\r\n @invalid=\"showInvalid = true\"\r\n />\r\n\r\n <div v-if=\"editor && !readonly && has('characterCount')\" class=\"rt-editor-footer\">\r\n <CharacterCount\r\n :min=\"options.characterCount?.min\"\r\n :limit=\"options.characterCount?.limit\"\r\n />\r\n </div>\r\n\r\n <MediaControls v-if=\"editor && !readonly && groups.mediaControls\" />\r\n\r\n <MentionMenu\r\n v-if=\"mentionMenu\"\r\n :state=\"mentionMenu.state\"\r\n @select=\"mentionMenu.select\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\n// Vue 2: `v-model` uses modelValue / update:modelValue, like in Vue 3\r\nexport default { model: { prop: \"modelValue\", event: \"update:modelValue\" } }\r\n</script>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, onBeforeUnmount, onMounted, ref, watch, watchEffect } from \"vue\"\r\nimport type { Editor, JSONContent } from \"@tiptap/core\"\r\nimport { EditorContent, useEditor } from \"@/lib/tiptap-vue\"\r\nimport { throttle } from \"@/lib/throttle\"\r\nimport { resizeImage } from \"@/lib/resize-image\"\r\nimport { afterPointerRelease } from \"@/lib/pointer\"\r\n\r\n// --- UI ---\r\nimport { Button } from \"@/components/tiptap-ui-primitive/button\"\r\nimport { Spacer } from \"@/components/tiptap-ui-primitive/spacer\"\r\nimport {\r\n Toolbar,\r\n ToolbarGroup,\r\n ToolbarSeparator,\r\n} from \"@/components/tiptap-ui-primitive/toolbar\"\r\nimport { HeadingDropdownMenu } from \"@/components/tiptap-ui/heading-dropdown-menu\"\r\nimport { ListDropdownMenu } from \"@/components/tiptap-ui/list-dropdown-menu\"\r\nimport CommandButton from \"@/components/tiptap-ui/CommandButton.vue\"\r\nimport { blockquoteCommand } from \"@/components/tiptap-ui/blockquote-button\"\r\nimport { codeBlockCommand } from \"@/components/tiptap-ui/code-block-button\"\r\nimport {\r\n ColorHighlightPopover,\r\n ColorHighlightPopoverContent,\r\n ColorHighlightPopoverButton,\r\n} from \"@/components/tiptap-ui/color-highlight-popover\"\r\nimport { LinkPopover, LinkContent, LinkButton } from \"@/components/tiptap-ui/link-popover\"\r\nimport { markCommand, type Mark } from \"@/components/tiptap-ui/mark-button\"\r\nimport { textAlignCommand, type TextAlign } from \"@/components/tiptap-ui/text-align-button\"\r\nimport { undoRedoCommand } from \"@/components/tiptap-ui/undo-redo-button\"\r\nimport type { ListType } from \"@/components/tiptap-ui/list-button\"\r\nimport Icon from \"@/components/tiptap-icons/Icon.vue\"\r\nimport { ArrowLeftIcon, HighlighterIcon, LinkIcon } from \"@/components/tiptap-icons\"\r\nimport MentionMenu from \"./MentionMenu.vue\"\r\nimport VisibilityConditionsMenu from \"./VisibilityConditionsMenu.vue\"\r\nimport UploadPopover from \"./UploadPopover.vue\"\r\nimport YoutubePopover from \"./YoutubePopover.vue\"\r\nimport TableMenu from \"./TableMenu.vue\"\r\nimport TemplatesMenu from \"./TemplatesMenu.vue\"\r\nimport MediaControls from \"./MediaControls.vue\"\r\nimport CharacterCount from \"./CharacterCount.vue\"\r\nimport TableOfContents from \"./TableOfContents.vue\"\r\n\r\n// --- Editor ---\r\nimport { provideTiptapEditor } from \"@/composables/use-tiptap-editor\"\r\nimport { provideTranslations } from \"@/composables/use-translations\"\r\nimport { useIsBreakpoint } from \"@/composables/use-is-breakpoint\"\r\nimport { useEditorState } from \"@/composables/use-editor-state\"\r\nimport { MAX_FILE_SIZE } from \"@/lib/tiptap-utils\"\r\nimport { matchesShortcut, SHORTCUTS } from \"@/lib/shortcuts\"\r\nimport {\r\n buildExtensions,\r\n DEFAULT_HEADING_LEVELS,\r\n resolveExtensions,\r\n resolveHiddenButtons,\r\n} from \"./extensions\"\r\nimport { createMentionMenu } from \"./extensions/mention-menu\"\r\nimport { refreshPreviews } from \"./extensions/previews\"\r\nimport { addLiquidConditions, stripLiquidConditions } from \"./liquid\"\r\n// Relative imports: these types end up in the published declaration files\r\nimport type {\r\n RichtextAction,\r\n RichtextExtensionName,\r\n RichtextError,\r\n RichtextLocale,\r\n RichtextOptions,\r\n RichtextToolbarPosition,\r\n RichtextValue,\r\n UploadFileFunction,\r\n} from \"./types\"\r\n\r\n// --- Styles (includes the template's node styles) ---\r\nimport \"./input-richtext.scss\"\r\n\r\n/**\r\n * Richtext editor based on Tiptap's Simple Editor template.\r\n *\r\n * Extensions are chosen once, when the editor is created. To change\r\n * `options` afterwards, re-mount the component (e.g. with a `key`).\r\n */\r\nconst props = withDefaults(\r\n defineProps<{\r\n modelValue?: RichtextValue\r\n /** Format of the emitted value (default \"html\"). HTML output wraps visibility conditions in liquid tags. */\r\n outputFormat?: \"html\" | \"json\"\r\n options?: RichtextOptions\r\n /** Values shown instead of a mention's label, keyed by mention id. Reactive. */\r\n mentionPreviews?: Record<string, unknown>\r\n /**\r\n * Uploads a file and resolves to its URL. Used by the image and file buttons and\r\n * for dropped/pasted files. Without it, the buttons only accept links and\r\n * dropped/pasted files fail with a console error.\r\n */\r\n onUploadFile?: UploadFileFunction\r\n /** Default: \"Begin met typen...\" / \"Start typing...\" */\r\n placeholder?: string\r\n /** Read-only. With \"tasks\", task list checkboxes can still be toggled, which emits the new value. */\r\n readonly?: boolean | \"tasks\"\r\n autofocus?: boolean\r\n /** Submitted with the surrounding form under this name */\r\n name?: string\r\n /** The form can't be submitted while the editor is empty */\r\n required?: boolean\r\n toolbarPosition?: RichtextToolbarPosition\r\n /** Smaller spacing; toolbar and character count only show while the editor has focus */\r\n compact?: boolean\r\n /** Language of the editor's own texts. Default \"nl\". */\r\n locale?: RichtextLocale\r\n }>(),\r\n {\r\n modelValue: \"\",\r\n outputFormat: \"html\",\r\n options: () => ({}),\r\n mentionPreviews: () => ({}),\r\n onUploadFile: undefined,\r\n placeholder: undefined,\r\n readonly: false,\r\n autofocus: false,\r\n name: undefined,\r\n required: false,\r\n toolbarPosition: \"top\",\r\n compact: false,\r\n locale: \"nl\",\r\n }\r\n)\r\n\r\nconst emit = defineEmits<{\r\n (e: \"update:modelValue\", value: RichtextValue): void\r\n (e: \"action\", action: RichtextAction): void\r\n (e: \"keydown-ctrl-enter\", event: KeyboardEvent): void\r\n (e: \"error\", error: RichtextError): void\r\n}>()\r\n\r\nconst t = provideTranslations(() => props.locale)\r\n\r\n// --- Extensions (fixed at creation) ---\r\n\r\nconst enabled = resolveExtensions(props.options)\r\nconst has = (name: RichtextExtensionName) => enabled.has(name)\r\n// Hidden buttons (`options.hidden`): the extension works, but has no button\r\nconst hiddenButtons = resolveHiddenButtons(props.options, enabled)\r\nconst showButton = (name: RichtextExtensionName) => has(name) && !hiddenButtons.has(name)\r\n\r\nconst headingLevels = props.options.heading?.levels ?? DEFAULT_HEADING_LEVELS\r\nconst listTypes = ([\"bulletList\", \"orderedList\", \"taskList\"] as ListType[]).filter(has)\r\n// Which toolbar groups have at least one button\r\nconst groups = {\r\n blocks:\r\n has(\"heading\") ||\r\n listTypes.length > 0 ||\r\n showButton(\"blockquote\") ||\r\n showButton(\"codeBlock\") ||\r\n showButton(\"visibilityConditions\"),\r\n marks: ([\"bold\", \"italic\", \"strike\", \"code\", \"underline\", \"highlight\", \"link\"] as const).some(showButton),\r\n scripts: showButton(\"superscript\") || showButton(\"subscript\"),\r\n align: showButton(\"textAlign\"),\r\n media: ([\"image\", \"file\", \"youtube\", \"table\", \"templates\"] as const).some(has),\r\n mediaControls: has(\"mediaControls\") && (has(\"image\") || has(\"youtube\")),\r\n}\r\n\r\nconst formattingMarks = ([\"bold\", \"italic\", \"strike\", \"code\", \"underline\"] as Mark[]).filter(showButton)\r\nconst textAligns: TextAlign[] = [\"left\", \"center\", \"right\", \"justify\"]\r\n\r\nconst mentionMenu = has(\"mention\") ? createMentionMenu() : null\r\n\r\n// --- Value in / out ---\r\n\r\n// HTML values come back with the liquid wrappers the editor added\r\nconst toEditorContent = (value: RichtextValue) =>\r\n typeof value === \"string\" ? stripLiquidConditions(value) : (value ?? \"\")\r\n\r\nconst getValue = (currentEditor: Editor): RichtextValue =>\r\n props.outputFormat === \"html\"\r\n ? addLiquidConditions(currentEditor.getHTML())\r\n : currentEditor.getJSON()\r\n\r\nlet lastEmitted: RichtextValue | undefined\r\n\r\nconst emitValue = throttle(() => {\r\n if (!editor.value) return\r\n lastEmitted = getValue(editor.value)\r\n emit(\"update:modelValue\", lastEmitted)\r\n}, 200)\r\n\r\n// Registered before useEditor, so the last change is emitted before the editor is destroyed\r\nonBeforeUnmount(() => emitValue.flush())\r\n\r\n/**\r\n * All uploads (image/file buttons, drop, paste) go through here: checks the\r\n * size, and emits `error` for anything that goes wrong before rethrowing.\r\n */\r\nconst DEFAULT_IMAGE_MAX_WIDTH = 1200\r\n\r\nconst uploadFile = async (file: File, type: \"file\" | \"image\") => {\r\n const fail = (message: string, cause?: unknown) => {\r\n const error = cause instanceof Error ? cause : new Error(message)\r\n emit(\"error\", { type: \"upload\", file, message: error.message, error })\r\n return error\r\n }\r\n\r\n if (!props.onUploadFile) {\r\n throw fail(\"<InputRichtext> needs an onUploadFile function to upload files\")\r\n }\r\n // Scaled down first, so a large photo can still end up under the size limit\r\n if (type === \"image\") {\r\n file = await resizeImage(file, props.options.image?.maxWidth ?? DEFAULT_IMAGE_MAX_WIDTH)\r\n }\r\n if (file.size > MAX_FILE_SIZE) {\r\n throw fail(t.value.upload.tooLarge(MAX_FILE_SIZE / 1024 / 1024))\r\n }\r\n try {\r\n return await props.onUploadFile(file, type)\r\n } catch (error) {\r\n throw fail(t.value.upload.failed, error)\r\n }\r\n}\r\n\r\n\r\nconst editor = useEditor({\r\n content: toEditorContent(props.modelValue),\r\n editable: !props.readonly,\r\n autofocus: props.autofocus,\r\n extensions: buildExtensions(enabled, props.options, {\r\n placeholder: () => props.placeholder ?? t.value.placeholder,\r\n mentionMenu,\r\n getMentionPreviews: () => props.mentionPreviews,\r\n onAction: (action) => emit(\"action\", action),\r\n uploadFile,\r\n canCheckTasksReadonly: () => props.readonly === \"tasks\",\r\n messages: () => t.value,\r\n }),\r\n editorProps: {\r\n handleKeyDown: (_view, event) => {\r\n if (event.key === \"Enter\" && (event.ctrlKey || event.metaKey)) {\r\n emit(\"keydown-ctrl-enter\", event)\r\n return true\r\n }\r\n return false\r\n },\r\n },\r\n onUpdate: () => emitValue(),\r\n})\r\n\r\nprovideTiptapEditor(editor)\r\n\r\nconst isSameValue = (a: RichtextValue | undefined, b: RichtextValue | undefined) =>\r\n a === b ||\r\n (typeof a === \"object\" &&\r\n typeof b === \"object\" &&\r\n JSON.stringify(a as JSONContent) === JSON.stringify(b as JSONContent))\r\n\r\n// Only load values that didn't come from the editor itself\r\nwatch(\r\n () => props.modelValue,\r\n (value) => {\r\n if (!editor.value || isSameValue(value, lastEmitted)) return\r\n emitValue.cancel()\r\n editor.value.commands.setContent(toEditorContent(value), { emitUpdate: false })\r\n lastEmitted = value\r\n }\r\n)\r\n\r\nwatch(\r\n () => props.readonly,\r\n (readonly) => editor.value?.setEditable(!readonly)\r\n)\r\n\r\nwatch(\r\n () => props.autofocus,\r\n (autofocus) => {\r\n if (autofocus) editor.value?.commands.focus()\r\n }\r\n)\r\n\r\n// The \"Block not visible\" text also follows the locale\r\nwatch(\r\n [() => props.mentionPreviews, () => props.locale],\r\n () => editor.value && refreshPreviews(editor.value),\r\n { deep: true }\r\n)\r\n\r\n// --- Compact: toolbar shown while focused ---\r\n\r\nconst rootRef = ref<HTMLElement | null>(null)\r\n\r\n/**\r\n * The toolbar shows and hides with focus, which moves on mousedown. Changing\r\n * the layout then would make the click land on whatever moved under the\r\n * pointer, so the toolbar keeps its state (data-toolbar-hold, see\r\n * input-richtext.scss) until the press and its click are done.\r\n */\r\nfunction holdToolbar() {\r\n const root = rootRef.value\r\n const toolbar = root?.querySelector(\".rt-editor-toolbar\")\r\n if (!props.compact || !root || !toolbar) return\r\n\r\n root.dataset.toolbarHold = getComputedStyle(toolbar).display === \"none\" ? \"hidden\" : \"shown\"\r\n afterPointerRelease(() => {\r\n delete root.dataset.toolbarHold\r\n })\r\n}\r\n\r\nonMounted(() => document.addEventListener(\"pointerdown\", holdToolbar, true))\r\nonBeforeUnmount(() => document.removeEventListener(\"pointerdown\", holdToolbar, true))\r\n\r\n// --- Native form validation ---\r\n\r\nconst minCharacters = props.options.characterCount?.min\r\nconst isFormField = computed(() => !!(props.name || props.required || minCharacters))\r\nconst formField = ref<HTMLTextAreaElement | null>(null)\r\nconst showInvalid = ref(false)\r\n\r\nconst validity = useEditorState(\r\n computed(() => editor.value ?? null),\r\n (currentEditor) => ({\r\n empty: currentEditor?.isEmpty ?? true,\r\n characters:\r\n (currentEditor?.storage.characterCount?.characters() as number | undefined) ?? 0,\r\n })\r\n)\r\n\r\n// Required is handled natively (the textarea is empty when the editor is);\r\n// the minimum length through a custom message, only when there is content\r\nconst tooShort = computed(\r\n () =>\r\n !!minCharacters && !validity.value.empty && validity.value.characters < minCharacters\r\n)\r\nconst isValid = computed(() => !(props.required && validity.value.empty) && !tooShort.value)\r\n\r\nwatchEffect(() => {\r\n formField.value?.setCustomValidity(\r\n tooShort.value\r\n ? t.value.validation.minCharacters(minCharacters ?? 0, validity.value.characters)\r\n : \"\"\r\n )\r\n})\r\n\r\n// The red border goes away as soon as the content is valid again\r\nwatch(isValid, (valid) => {\r\n if (valid) showInvalid.value = false\r\n})\r\n\r\n// After a failed submit the browser focuses the hidden field and shows its\r\n// message there. Moving focus right away would close that message (it closes on\r\n// blur), so focus moves into the editor on the first key instead.\r\nfunction onFormFieldKeydown(event: KeyboardEvent) {\r\n if (event.key === \"Tab\") return\r\n editor.value?.view.focus()\r\n}\r\n\r\n// With a `name`, the value is added to the form's data on submit. Taken fresh\r\n// from the editor, so a change from the last 200ms isn't missed.\r\nwatch(\r\n () => (props.name ? formField.value?.form : null),\r\n (form, _previous, onCleanup) => {\r\n if (!form) return\r\n const onFormData = (event: FormDataEvent) => {\r\n if (!editor.value || !props.name) return\r\n const value = getValue(editor.value)\r\n event.formData.set(props.name, typeof value === \"string\" ? value : JSON.stringify(value))\r\n }\r\n form.addEventListener(\"formdata\", onFormData)\r\n onCleanup(() => form.removeEventListener(\"formdata\", onFormData))\r\n },\r\n { flush: \"post\" }\r\n)\r\n\r\n// --- Keyboard shortcuts (see lib/shortcuts.ts) ---\r\n\r\n// Only while focus is inside this editor, so several editors on a page don't\r\n// all react. Tiptap's own shortcuts (bold, lists, …) are handled before this.\r\nfunction onKeydown(event: KeyboardEvent) {\r\n const currentEditor = editor.value\r\n if (event.defaultPrevented || !currentEditor) return\r\n const root = event.currentTarget as HTMLElement\r\n\r\n if (matchesShortcut(event, SHORTCUTS.focusToolbar)) {\r\n event.preventDefault()\r\n root\r\n .querySelector<HTMLElement>(\r\n '.tiptap-toolbar [tabindex=\"0\"]:not([disabled]), .tiptap-toolbar button:not([disabled])'\r\n )\r\n ?.focus()\r\n return\r\n }\r\n\r\n // Escape in the toolbar goes back to the text\r\n if (event.key === \"Escape\" && (event.target as HTMLElement).closest(\".tiptap-toolbar\")) {\r\n currentEditor.commands.focus()\r\n return\r\n }\r\n\r\n if (matchesShortcut(event, SHORTCUTS.clearFormatting)) {\r\n event.preventDefault()\r\n // All text formatting, but links stay\r\n Object.keys(currentEditor.schema.marks)\r\n .filter((mark) => mark !== \"link\")\r\n .reduce((chain, mark) => chain.unsetMark(mark), currentEditor.chain().focus())\r\n .run()\r\n return\r\n }\r\n\r\n // Buttons that open a popover or menu declare their shortcut\r\n const trigger = Array.from(\r\n root.querySelectorAll<HTMLButtonElement>(\"button[data-rt-shortcut]\")\r\n ).find((button) => !button.disabled && matchesShortcut(event, button.dataset.rtShortcut!))\r\n if (!trigger) return\r\n\r\n event.preventDefault()\r\n trigger.click()\r\n returnFocusOnClose(trigger)\r\n}\r\n\r\n/**\r\n * After a popover opened by a shortcut closes (Escape, applying, clicking a\r\n * menu item), typing continues in the editor, unless focus moved elsewhere.\r\n */\r\nfunction returnFocusOnClose(trigger: HTMLElement) {\r\n const popoverId = trigger.getAttribute(\"popovertarget\")\r\n const popover = popoverId ? document.getElementById(popoverId) : null\r\n if (!popover) return\r\n\r\n const onToggle = (event: Event) => {\r\n if ((event as ToggleEvent).newState !== \"closed\") return\r\n popover.removeEventListener(\"toggle\", onToggle)\r\n const active = document.activeElement\r\n if (!active || active === document.body || trigger.contains(active)) {\r\n editor.value?.commands.focus()\r\n }\r\n }\r\n popover.addEventListener(\"toggle\", onToggle)\r\n}\r\n\r\n// --- Mobile: highlight and link open as a separate toolbar view ---\r\n\r\nconst isMobile = useIsBreakpoint()\r\nconst mobileView = ref<\"main\" | \"highlighter\" | \"link\">(\"main\")\r\n\r\nwatch(isMobile, (mobile) => {\r\n if (!mobile) mobileView.value = \"main\"\r\n})\r\n\r\ndefineExpose({\r\n editor: computed(() => editor.value),\r\n /** Same as a form field's checkValidity(); true when not part of form validation */\r\n checkValidity: () => formField.value?.checkValidity() ?? true,\r\n})\r\n</script>\r\n","import InputRichtext from \"./components/input-richtext/InputRichtext.vue\"\r\n\r\nexport { InputRichtext }\r\nexport {\r\n addLiquidConditions,\r\n stripLiquidConditions,\r\n} from \"./components/input-richtext/liquid\"\r\nexport type * from \"./components/input-richtext/types\"\r\n\r\n/**\r\n * Registers <InputRichtext> globally: `app.use(RichtextPlugin)`.\r\n * Or import `InputRichtext` and register it locally.\r\n */\r\nexport const RichtextPlugin = {\r\n // `app` is a Vue 3 app, or the Vue 2 constructor (Vue.use)\r\n install(app: { component: (name: string, component: any) => unknown }) {\r\n app.component(\"InputRichtext\", InputRichtext)\r\n },\r\n}\r\n\r\nexport default RichtextPlugin\r\n"],"names":["useEditor","options","editor","shallowRef","onMounted","Editor","onBeforeUnmount","throttle","fn","wait","timer","pending","run","throttled","next","RESIZABLE_TYPES","resizeImage","file","maxWidth","bitmap","width","height","canvas","context","blob","resolve","pointerDown","release","isPointerDown","afterPointerRelease","callback","done","MAX_FILE_SIZE","MAC_SYMBOLS","isMac","formatShortcutKey","key","capitalize","lowerKey","parseShortcutKeys","props","shortcutKeys","delimiter","isMarkInSchema","markName","isNodeInSchema","nodeName","isValidPosition","pos","warnedMissingExtensions","isExtensionAvailable","extensionNames","names","extensions","found","name","ext","findNodeAtPosition","position","node","error","findNodePosition","nodePos","hasValidNode","hasValidPos","foundPos","foundNode","currentNode","nodeAtPos","isNodeTypeSelected","nodeTypeNames","checkAncestorNodes","selection","NodeSelection","selectedNode","$from","depth","ancestorNode","selectionWithinConvertibleTypes","types","state","allowed","nodeType","TextSelection","AllSelection","valid","ATTR_WHITESPACE","isAllowedUri","uri","protocols","allowedProtocols","sanitizeUrl","inputUrl","baseUrl","url","getSelectedBlockNodes","doc","from","to","blocks","seen","title","computed","shortcut","style","en","level","text","megabytes","label","count","min","current","nl","DEFAULT_LOCALE","locales","TranslationsKey","provideTranslations","locale","messages","provide","useTranslations","inject","resolveValue","value","isRef","shallowEqual","a","b","keysA","keysB","useMenuNavigation","containerRef","query","items","onSelect","onClose","orientation","autoSelectFirstItem","loopOnTab","index","ref","setSelectedIndex","handleKeyboardNavigation","event","list","moveNext","movePrev","watch","target","_previous","onCleanup","t","toolbarRef","expose","initialContainerTabIndex","observer","collectItems","selectedIndex","el","handleFocus","e","toolbar","itemIndex","handleBlur","nextTarget","updateItems","currentIndex","focusIsInside","item","existingTabStop","activeIndex","i","TIPTAP_EDITOR_KEY","provideTiptapEditor","useTiptapEditor","providedEditor","injectedEditor","EDITOR_EVENTS","useEditorState","selector","deps","equals","update","currentEditor","toStateRefs","refs","commands","defineCommand","create","command","AlignCenterIcon","AlignJustifyIcon","AlignLeftIcon","AlignRightIcon","ArrowLeftIcon","BanIcon","BlockquoteIcon","BoldIcon","ChevronDownIcon","CodeBlockIcon","Code2Icon","CornerDownLeftIcon","ExternalLinkIcon","HeadingFiveIcon","HeadingFourIcon","HeadingIcon","HeadingOneIcon","HeadingSixIcon","HeadingThreeIcon","HeadingTwoIcon","HighlighterIcon","ImagePlusIcon","ItalicIcon","LinkIcon","ListIcon","ListOrderedIcon","ListTodoIcon","Redo2Icon","StrikeIcon","SubscriptIcon","SuperscriptIcon","TrashIcon","UnderlineIcon","Undo2Icon","EyeIcon","PaperclipIcon","YoutubeIcon","TableIcon","FileTextIcon","headingIcons","HEADING_SHORTCUT_KEYS","canToggle","turnInto","isHeadingActive","l","toggleHeading","levels","toggleLevel","view","tr","isPossibleToTurnInto","chain","firstChild","lastChild","resolvedFrom","resolvedTo","headingCommand","getActiveHeadingLevel","sprite","SPRITE_ID","container","isActive","canRun","uid","supportsAnchorPositioning","usePopover","open","setOpen","align","sideOffset","id","anchorName","contentRef","isShown","positionWithoutAnchor","content","trigger","rect","left","show","onToggle","nextOpen","nextTick","triggerAttrs","contentAttrs","instance","getCurrentInstance","element","__default__","close","emit","getItems","onKeyDown","focusAt","onClick","isOpen","activeLevel","canToggleHeading","listIcons","LIST_SHORTCUT_KEYS","canToggleList","type","isListActive","toggleList","toggle","listCommand","listOptions","canToggleAnyList","listTypes","isAnyListActive","getFilteredListOptions","availableTypes","option","getActiveListType","activeType","BLOCKQUOTE_SHORTCUT_KEY","canToggleBlockquote","toggleBlockquote","blockquoteCommand","CODE_BLOCK_SHORTCUT_KEY","toggleCodeBlock","codeBlockCommand","COLOR_HIGHLIGHT_SHORTCUT_KEY","HIGHLIGHT_COLORS","pickHighlightColorsByValue","values","colorMap","color","canColorHighlight","isColorHighlightActive","highlightColor","toggleColorHighlight","highlightMarkType","removeHighlight","highlightCommand","removeHighlightCommand","useIsBreakpoint","mode","breakpoint","matches","mql","onChange","isMobile","cardRef","canHighlight","SHORTCUTS","matchesShortcut","keys","parts","wants","modifier","mod","LINK_SHORTCUT_KEY","canSetLink","isLinkActive","shouldShowLinkButton","hideWhenUnavailable","linkInSchema","useLinkHandler","onSetLink","openInNewTab","syncUrl","href","setLink","isEmpty","removeLink","toggleNewTab","openLink","features","safeUrl","useLinkState","useLinkPopover","config","linkState","linkHandler","inputRef","isVisible","canSet","isFocused","icon","shouldAutoOpen","onKeydown","handleSetLink","markIcons","MARK_SHORTCUT_KEYS","canToggleMark","isMarkActive","toggleMark","markCommand","TEXT_ALIGN_SHORTCUT_KEYS","textAlignIcons","canSetTextAlign","hasSetTextAlign","isTextAlignActive","setTextAlign","textAlignCommand","UNDO_REDO_SHORTCUT_KEYS","historyIcons","canExecuteUndoRedoAction","action","executeUndoRedoAction","undoRedoCommand","useTopLayer","onShown","OFFSET","menuRef","menuSize","measure","PREVIEWS_CHANGED","refreshPreviews","VISIBILITY_CONDITION_NODE","hiddenKey","PluginKey","conditionPasses","hiddenDecorations","previews","DecorationSet","decorations","condition","Decoration","VisibilityConditions","Node","attributes","HTMLAttributes","mergeAttributes","attrs","Plugin","_","activeCondition","fileNameFromUrl","path","FileNode","isUploading","fileInput","canInsert","nodeHref","insert","insertUrl","onFileChosen","input","uploadError","openSelected","removeSelected","src","insertVideo","removeVideo","insertOptions","isInTable","groups","c","CONDITION_SELECTOR","liquidIf","LIQUID_ENDIF","IF_PATTERN","parse","html","template","addLiquidConditions","conditions","block","stripLiquidConditions","trim","trimText","original","start","escapeHtml","toContent","line","MEDIA_TYPES","GAP","SIZES","FLOATS","focusInside","caption","media","onFocusOut","setAttributes","size","dom","above","onViewportChange","characters","headingPositions","positions","lastDoc","lastHeadings","headings","nodes","top","scrollTo","MAX_ITEMS","previewsKey","previewDecorations","preview","group","search","suggestion","prefix","rest","hasGroup","createMention","menu","getPreviews","onAction","entries","groupsByChar","mentionText","char","Mention","toDOM","DOMSerializer","part","className","range","groupName","entry","groupClass","imageClass","attribute","pattern","mediaAttributes","placeholderKey","blockPosition","$pos","placeholderDom","onDismiss","FileDrop","Extension","dispatch","findPlaceholder","spec","typeOf","accepted","files","uploadFile","placeholder","uploadFiles","at","placeholders","set","message","_slice","moved","data","Link","TiptapLink","createTaskItem","canCheckReadonly","TiptapTaskItem","createView","checkbox","getPos","HeadingId","DEFAULT_EXTENSIONS","youtubeRatio","DEFAULT_HEADING_LEVELS","HIDEABLE_BUTTONS","SUPPORTED_EXTENSIONS","resolveHiddenButtons","enabled","hidden","unsupported","resolveExtensions","buildExtensions","has","hasBasicList","StarterKit","Selection","TextAlign","TaskList","Highlight","Subscript","Superscript","Typography","Placeholder","Image","TableKit","CharacterCount","Youtube","createMentionMenu","reactive","sync","select","hiddenButtons","showButton","headingLevels","formattingMarks","textAligns","mentionMenu","toEditorContent","getValue","lastEmitted","emitValue","DEFAULT_IMAGE_MAX_WIDTH","fail","cause","_view","isSameValue","readonly","autofocus","rootRef","holdToolbar","root","minCharacters","isFormField","formField","showInvalid","validity","tooShort","isValid","watchEffect","onFormFieldKeydown","form","onFormData","mark","button","returnFocusOnClose","popoverId","popover","active","mobileView","mobile","RichtextPlugin","app","InputRichtext"],"mappings":"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"}