diff --git a/package.json b/package.json
index 5a06a546..91a770e1 100644
--- a/package.json
+++ b/package.json
@@ -64,6 +64,17 @@
"@codemirror/view": "^6.36.4",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@tailwindcss/forms": "^0.5.10",
+ "@tiptap/core": "^2.14.0",
+ "@tiptap/extension-bubble-menu": "^2.14.0",
+ "@tiptap/extension-dropcursor": "^2.14.0",
+ "@tiptap/extension-image": "^2.14.0",
+ "@tiptap/extension-link": "^2.14.0",
+ "@tiptap/extension-placeholder": "^2.14.0",
+ "@tiptap/extension-task-item": "^2.14.0",
+ "@tiptap/extension-task-list": "^2.14.0",
+ "@tiptap/extension-typography": "^2.14.0",
+ "@tiptap/starter-kit": "^2.14.0",
+ "@tiptap/suggestion": "^2.14.0",
"@tsconfig/svelte": "^5.0.4",
"@types/chrome": "^0.0.308",
"@types/color": "^4.2.0",
@@ -92,6 +103,7 @@
"mathjs": "^14.4.0",
"million": "^3.1.11",
"motion": "^12.4.12",
+ "motion-start": "^0.1.15",
"postcss": "^8.5.3",
"react": "17",
"react-best-gradient-color-picker": "3.0.11",
@@ -99,6 +111,7 @@
"rss-parser": "^3.13.0",
"sortablejs": "^1.15.6",
"svelte": "^5.22.6",
+ "svelte-hero-icons": "^5.2.0",
"typescript": "^5.8.2",
"uuid": "^11.1.0",
"vite": "^6.2.1",
diff --git a/src/pageState.js b/src/pageState.js
index 8ee66117..5ca7cfd6 100644
--- a/src/pageState.js
+++ b/src/pageState.js
@@ -222,5 +222,23 @@ window.addEventListener("message", (event) => {
});
document.dispatchEvent(keyboardEvent);
+ } else if (event.data.type === "ckeditorSetData") {
+ // Handle CKEditor data setting
+ const { editorId, content } = event.data;
+
+ if (window.CKEDITOR && window.CKEDITOR.instances && window.CKEDITOR.instances[editorId]) {
+ window.CKEDITOR.instances[editorId].setData(content);
+ } else {
+ console.warn(`[pageState] CKEditor instance '${editorId}' not found`);
+ }
+ } else if (event.data.type === "ckeditorGetData") {
+ const { editorId } = event.data;
+ if (window.CKEDITOR && window.CKEDITOR.instances && window.CKEDITOR.instances[editorId]) {
+ const data = window.CKEDITOR.instances[editorId].getData();
+ window.postMessage({
+ type: "ckeditorGetDataResponse",
+ data,
+ }, "*");
+ }
}
});
diff --git a/src/plugins/built-in/customMessageEditor/BetterEditor.svelte b/src/plugins/built-in/customMessageEditor/BetterEditor.svelte
new file mode 100644
index 00000000..c3e45fb7
--- /dev/null
+++ b/src/plugins/built-in/customMessageEditor/BetterEditor.svelte
@@ -0,0 +1,65 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/plugins/built-in/customMessageEditor/Editor/Editor.svelte b/src/plugins/built-in/customMessageEditor/Editor/Editor.svelte
new file mode 100644
index 00000000..a0c513cb
--- /dev/null
+++ b/src/plugins/built-in/customMessageEditor/Editor/Editor.svelte
@@ -0,0 +1,154 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/plugins/built-in/customMessageEditor/Editor/EditorOverrideStyles.css b/src/plugins/built-in/customMessageEditor/Editor/EditorOverrideStyles.css
new file mode 100644
index 00000000..3b6bcb00
--- /dev/null
+++ b/src/plugins/built-in/customMessageEditor/Editor/EditorOverrideStyles.css
@@ -0,0 +1,398 @@
+.editor-prose {
+ font-family:
+ ui-sans-serif,
+ system-ui,
+ -apple-system,
+ BlinkMacSystemFont,
+ 'Segoe UI',
+ Roboto,
+ 'Helvetica Neue',
+ Arial,
+ 'Noto Sans',
+ sans-serif !important;
+ line-height: 1.6 !important;
+ color: #374151 !important;
+ font-size: 14px !important;
+ border: none !important;
+ padding: 0 !important;
+ margin: 0 !important;
+ box-sizing: border-box !important;
+
+ .dark & * {
+ color: #d1d5db !important;
+ }
+
+ * {
+ color: #374151 !important;
+ }
+
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6,
+ p,
+ ul,
+ ol {
+ width: 100% !important;
+ min-width: 2px !important;
+ box-sizing: border-box !important;
+ }
+
+ h1 {
+ font-size: 1.5rem !important;
+ font-weight: 700 !important;
+ margin: 0.75rem 0 0.5rem 0 !important;
+ line-height: 1.3 !important;
+ color: #111827 !important;
+ padding: 0 !important;
+ border: none !important;
+ background: none !important;
+ text-shadow: none !important;
+
+ .dark & {
+ color: #f9fafb !important;
+ }
+ }
+
+ h2 {
+ font-size: 1.25rem !important;
+ font-weight: 600 !important;
+ margin: 0.6rem 0 0.4rem 0 !important;
+ line-height: 1.4 !important;
+ color: #1f2937 !important;
+ padding: 0 !important;
+ border: none !important;
+ background: none !important;
+ text-shadow: none !important;
+
+ .dark & {
+ color: #e5e7eb !important;
+ }
+ }
+
+ h3 {
+ font-size: 1.125rem !important;
+ font-weight: 600 !important;
+ margin: 0.5rem 0 0.3rem 0 !important;
+ line-height: 1.4 !important;
+ color: #374151 !important;
+ padding: 0 !important;
+ border: none !important;
+ background: none !important;
+ text-shadow: none !important;
+
+ .dark & {
+ color: #d1d5db !important;
+ }
+ }
+
+ p {
+ margin: 0.4rem 0 !important;
+ line-height: 1.6 !important;
+ font-size: 0.875rem !important;
+ padding: 0 !important;
+ border: none !important;
+ background: none !important;
+ color: inherit !important;
+ text-shadow: none !important;
+ }
+
+ ul {
+ margin: 0.5rem 0 !important;
+ padding-left: 1.25rem !important;
+ list-style-type: disc !important;
+ border: none !important;
+ background: none !important;
+
+ ul {
+ list-style-type: circle !important;
+
+ ul {
+ list-style-type: square !important;
+ }
+ }
+
+ &[data-type='taskList'] {
+ list-style: none !important;
+ padding: 0 !important;
+ margin: 0.5rem 0 !important;
+ border: none !important;
+ background: none !important;
+
+ p {
+ margin: 0 !important;
+ font-size: 0.875rem !important;
+ line-height: 1.5 !important;
+ padding: 0 !important;
+ border: none !important;
+ background: none !important;
+ color: inherit !important;
+ text-shadow: none !important;
+ }
+
+ li {
+ display: flex !important;
+ align-items: flex-start !important;
+ margin: 0.25rem 0 !important;
+ padding: 0 !important;
+ border: none !important;
+ background: none !important;
+ color: inherit !important;
+ text-shadow: none !important;
+ list-style: none !important;
+
+ > label {
+ flex: 0 0 auto !important;
+ margin-right: 0.5rem !important;
+ margin-top: 0.125rem !important;
+ user-select: none !important;
+ padding: 0 !important;
+ border: none !important;
+ background: none !important;
+
+ input[type='checkbox'] {
+ width: 1rem !important;
+ height: 1rem !important;
+ border-radius: 0.25rem !important;
+ border: 2px solid #d1d5db !important;
+ background-color: #fff !important;
+ cursor: pointer !important;
+ appearance: none !important;
+ -webkit-appearance: none !important;
+ -moz-appearance: none !important;
+ position: relative !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ box-shadow: none !important;
+
+ &:hover {
+ border-color: #3b82f6 !important;
+ }
+
+ &:checked {
+ background-color: #3b82f6 !important;
+ border-color: #3b82f6 !important;
+
+ &::after {
+ content: '' !important;
+ position: absolute !important;
+ left: 0.125rem !important;
+ top: 0.0625rem !important;
+ width: 0.375rem !important;
+ height: 0.625rem !important;
+ border: 2px solid white !important;
+ border-top: 0 !important;
+ border-left: 0 !important;
+ transform: rotate(45deg) !important;
+ }
+ }
+
+ .dark & {
+ border-color: #4b5563 !important;
+ background-color: #374151 !important;
+
+ &:hover {
+ border-color: #60a5fa !important;
+ }
+
+ &:checked {
+ background-color: #60a5fa !important;
+ border-color: #60a5fa !important;
+ }
+ }
+ }
+ }
+
+ > div {
+ flex: 1 1 auto !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ border: none !important;
+ background: none !important;
+ color: inherit !important;
+ }
+ }
+ }
+
+ li {
+ margin: 0.25rem 0 !important;
+ line-height: 1.5 !important;
+ font-size: 0.875rem !important;
+ display: list-item !important;
+ list-style-type: disc !important;
+ padding: 0 !important;
+ border: none !important;
+ background: none !important;
+ color: inherit !important;
+ text-shadow: none !important;
+ }
+ }
+
+ ol {
+ margin: 0.5rem 0 !important;
+ padding-left: 1.25rem !important;
+ list-style-type: decimal !important;
+ border: none !important;
+ background: none !important;
+
+ li {
+ margin: 0.25rem 0 !important;
+ line-height: 1.5 !important;
+ font-size: 0.875rem !important;
+ display: list-item !important;
+ list-style-type: decimal !important;
+ padding: 0 !important;
+ border: none !important;
+ background: none !important;
+ color: inherit !important;
+ text-shadow: none !important;
+ }
+ }
+
+ strong {
+ font-weight: 600 !important;
+ color: #111827 !important;
+ text-shadow: none !important;
+
+ .dark & {
+ color: #f9fafb !important;
+ }
+ }
+
+ em {
+ font-style: italic !important;
+ text-shadow: none !important;
+ }
+
+ a {
+ color: #3b82f6 !important;
+ text-decoration: underline !important;
+ text-decoration-color: rgba(59, 130, 246, 0.3) !important;
+ text-shadow: none !important;
+ background: none !important;
+ border: none !important;
+ padding: 0 !important;
+ margin: 0 !important;
+
+ &:hover {
+ text-decoration-color: #3b82f6 !important;
+ background: none !important;
+ }
+
+ .dark & {
+ color: #60a5fa !important;
+
+ &:hover {
+ text-decoration-color: #60a5fa !important;
+ }
+ }
+ }
+
+ blockquote {
+ padding: 0.2rem 1rem !important;
+ margin: 1rem 0 !important;
+ font-style: italic !important;
+ color: #6b7280 !important;
+ text-align: left !important;
+ border-right: none !important;
+ border-top: none !important;
+ border-bottom: none !important;
+ box-shadow: none !important;
+ text-shadow: none !important;
+ position: relative;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 4px;
+ height: 100%;
+ background-color: #d1d5db;
+ z-index: 1;
+ border-radius: 0.5rem;
+ }
+
+ .dark &::before {
+ background-color: #4b5563;
+ }
+
+ .dark & {
+ color: #9ca3af !important;
+ }
+ }
+
+ pre {
+ background-color: #f3f4f6 !important;
+ color: #1f2937 !important;
+ padding: 1rem !important;
+ border-radius: 0.5rem !important;
+ margin: 1rem 0 !important;
+ overflow-x: auto !important;
+ font-family:
+ ui-monospace, SFMono-Regular, 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace !important;
+ font-size: 0.875rem !important;
+ line-height: 1.5 !important;
+ text-align: left !important;
+ white-space: pre !important;
+ border: none !important;
+ box-shadow: none !important;
+ text-shadow: none !important;
+
+ .dark & {
+ background-color: rgba(35, 36, 41, 0.5) !important;
+ border: 1px solid rgba(35, 36, 41, 0.5) !important;
+ color: #e5e7eb !important;
+ }
+
+ code {
+ background-color: transparent !important;
+ color: inherit !important;
+ padding: 0 !important;
+ border-radius: 0 !important;
+ font-size: inherit !important;
+ font-family: inherit !important;
+ border: none !important;
+ margin: 0 !important;
+
+ .dark & {
+ background-color: transparent !important;
+ color: inherit !important;
+ }
+ }
+ }
+
+ hr {
+ border: none !important;
+ border-top: 1px solid #e5e7eb !important;
+ margin: 1rem 0 !important;
+ width: 100% !important;
+ background: none !important;
+ height: 0 !important;
+ padding: 0 !important;
+
+ .dark & {
+ border-top-color: #3f4854 !important;
+ }
+ }
+
+ code {
+ background-color: #f3f4f6 !important;
+ color: #d97706 !important;
+ padding: 0.125rem 0.25rem !important;
+ border-radius: 0.25rem !important;
+ font-size: 0.8125rem !important;
+ font-family:
+ ui-monospace, SFMono-Regular, 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace !important;
+ border: none !important;
+ margin: 0 !important;
+ text-shadow: none !important;
+
+ .dark & {
+ background-color: #374151 !important;
+ color: #fbbf24 !important;
+ }
+ }
+}
diff --git a/src/plugins/built-in/customMessageEditor/Editor/EditorStyles.css b/src/plugins/built-in/customMessageEditor/Editor/EditorStyles.css
new file mode 100644
index 00000000..33f86331
--- /dev/null
+++ b/src/plugins/built-in/customMessageEditor/Editor/EditorStyles.css
@@ -0,0 +1,256 @@
+/* Editor-specific styles (animations, transitions, editor-only features) - !these are not applied to sent messages! */
+
+/* Nested content styling with animated borders */
+.editor-prose li > *:not(:first-child) {
+ position: relative;
+ margin-left: -0.5rem;
+}
+
+.editor-prose li:not(:has(> label)) > *:not(:first-child)::before {
+ content: '';
+ position: absolute;
+ left: -0.75rem;
+ top: 0;
+ bottom: 0;
+ width: 1.5px;
+ background-color: #e5e7eb7e;
+ transform-origin: top;
+ animation: expandDown 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.dark .editor-prose li > *:not(:first-child)::before {
+ background-color: #4b55637b;
+}
+
+/* Special handling for nested lists to extend the line properly */
+.editor-prose li > ul,
+.editor-prose li > ol {
+ margin-left: -0.5rem;
+}
+
+.editor-prose li > ul::before,
+.editor-prose li > ol::before {
+ bottom: -0.25rem; /* Extend slightly below for better visual connection */
+}
+
+@keyframes expandDown {
+ 0% {
+ transform: scaleY(0);
+ opacity: 0;
+ }
+ 100% {
+ transform: scaleY(1);
+ opacity: 1;
+ }
+}
+
+/* Placeholders for editor-only */
+.editor-prose p::before,
+.editor-prose h1::before,
+.editor-prose h2::before,
+.editor-prose h3::before,
+.editor-prose h4::before,
+.editor-prose h5::before,
+.editor-prose h6::before {
+ content: attr(data-placeholder);
+ color: #9ca3af;
+ float: left;
+ height: 0;
+}
+
+.dark .editor-prose p::before,
+.dark .editor-prose h1::before,
+.dark .editor-prose h2::before,
+.dark .editor-prose h3::before,
+.dark .editor-prose h4::before,
+.dark .editor-prose h5::before,
+.dark .editor-prose h6::before {
+ color: #6b7280;
+}
+
+.bnEditor {
+ outline: none;
+ padding-inline: 50px;
+ border-radius: 8px;
+
+ /* Define a set of colors to be used throughout the app for consistency
+ see https://atlassian.design/foundations/color for more info */
+ --N800: #172b4d; /* Dark neutral used for tooltips and text on light background */
+ --N40: #dfe1e6; /* Light neutral used for subtle borders and text on dark background */
+}
+
+/*
+bnRoot should be applied to all top-level elements
+
+This includes the Prosemirror editor, but also element such as
+Tippy popups that are appended to document.body directly
+*/
+.bnRoot {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.bnRoot *,
+.bnRoot *::before,
+.bnRoot *::after {
+ -webkit-box-sizing: inherit;
+ -moz-box-sizing: inherit;
+ box-sizing: inherit;
+}
+
+/* reset styles, they will be set on blockContent */
+.defaultStyles p,
+.defaultStyles h1,
+.defaultStyles h2,
+.defaultStyles h3,
+.defaultStyles li {
+ all: unset !important;
+ margin: 0;
+ padding: 0;
+ font-size: inherit;
+ /* min width to make sure cursor is always visible */
+ min-width: 2px !important;
+}
+
+.defaultStyles {
+ font-size: 16px;
+ font-weight: normal;
+ font-family:
+ 'Inter',
+ 'SF Pro Display',
+ -apple-system,
+ BlinkMacSystemFont,
+ 'Open Sans',
+ 'Segoe UI',
+ 'Roboto',
+ 'Oxygen',
+ 'Ubuntu',
+ 'Cantarell',
+ 'Fira Sans',
+ 'Droid Sans',
+ 'Helvetica Neue',
+ sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.dragPreview {
+ position: absolute;
+ top: -1000px;
+}
+
+@keyframes fadeInScale {
+ 0% {
+ opacity: 0;
+ transform: scale(0.95);
+ }
+ 100% {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+/* Animate headers only */
+.editor-prose h1,
+.editor-prose h2,
+.editor-prose h3,
+.editor-prose h4,
+.editor-prose h5,
+.editor-prose h6 {
+ animation: fadeInScale 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+ transform-origin: left center;
+}
+
+/* Smooth transitions for all interactive elements */
+.editor-prose {
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+/* Bold and italic transitions */
+.editor-prose strong,
+.editor-prose em {
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+/* Selected node styling (Notion-like) */
+.ProseMirror-selectednode {
+ box-shadow: 0 0 0 4px #3b82f6;
+ border-radius: 4px;
+ background-color: rgba(59, 130, 246, 0.05);
+ transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
+ position: relative;
+}
+
+.dark .ProseMirror-selectednode {
+ box-shadow: 0 0 0 2px #3a3e44;
+ background-color: rgba(96, 165, 250, 0.08);
+}
+
+/* Ensure selected nodes have proper spacing */
+.ProseMirror-selectednode {
+ margin: 2px;
+}
+
+/* Drag and drop containment */
+.editor-prose {
+ position: relative;
+ overflow: hidden;
+ contain: layout style;
+}
+
+/* Image drag styling */
+.editor-prose img.tiptap-image {
+ cursor: grab;
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+ border-radius: 4px;
+ max-width: 100%;
+ height: auto;
+}
+
+.editor-prose img.tiptap-image:hover {
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ transform: scale(1.02);
+}
+
+.editor-prose img.tiptap-image:active {
+ cursor: grabbing;
+ transform: scale(0.98);
+}
+
+/* Dropcursor styling */
+.tiptap-dropcursor {
+ pointer-events: none;
+ border-radius: 2px;
+ animation: pulse 1.5s ease-in-out infinite;
+}
+
+@keyframes pulse {
+ 0%, 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.5;
+ }
+}
+
+/* Prevent drag operations outside editor */
+.editor-prose * {
+ -webkit-user-drag: auto;
+ -moz-user-drag: auto;
+ user-drag: auto;
+}
+
+/* Ensure only images within editor are draggable */
+.editor-prose img {
+ -webkit-user-drag: element;
+ -moz-user-drag: element;
+ user-drag: element;
+}
+
+/* Prevent text selection during drag */
+.editor-prose.ProseMirror-dragover * {
+ user-select: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+}
diff --git a/src/plugins/built-in/customMessageEditor/Editor/Plugins/BubbleMenu.svelte b/src/plugins/built-in/customMessageEditor/Editor/Plugins/BubbleMenu.svelte
new file mode 100644
index 00000000..c2b31314
--- /dev/null
+++ b/src/plugins/built-in/customMessageEditor/Editor/Plugins/BubbleMenu.svelte
@@ -0,0 +1,196 @@
+
+
+
+
+
+
+ {#if editor}
+
+
+
+
showTurnInto = !showTurnInto}
+ class="flex gap-1 items-center px-3 py-2 text-sm rounded-md transition-colors hover:bg-zinc-100 dark:hover:bg-zinc-800"
+ title="Turn into"
+ whileHover={{ scale: 1.02 }}
+ whileTap={{ scale: 0.98 }}
+ >
+ {getCurrentBlockType()}
+
+
+
+
+
+ {#if showTurnInto}
+
+ {#each turnIntoOptions as option}
+ {#if option.id === 'separator'}
+
+ {:else}
+ turnInto(option.id)}
+ class="flex gap-2 items-center px-3 py-2 w-full text-sm text-left transition-colors hover:bg-zinc-100/60 dark:hover:bg-zinc-700/40"
+ >
+ {option.icon}
+ {option.label}
+
+ {/if}
+ {/each}
+
+ {/if}
+
+
+
+
+ editor.chain().focus().toggleBold().run()}
+ class="p-2 rounded-md transition-colors hover:bg-zinc-100 dark:hover:bg-zinc-800 {editor.isActive('bold') ? 'bg-zinc-200 dark:bg-zinc-700' : '' }"
+ title="Bold"
+ whileHover={{ scale: 1.1 }}
+ whileTap={{ scale: 0.9 }}
+ >
+
+
+
+ editor.chain().focus().toggleItalic().run()}
+ class="p-2 rounded-md transition-colors hover:bg-zinc-100 dark:hover:bg-zinc-800 {editor.isActive('italic') ? 'bg-zinc-200 dark:bg-zinc-700' : '' }"
+ title="Italic"
+ whileHover={{ scale: 1.1 }}
+ whileTap={{ scale: 0.9 }}
+ >
+
+
+
+ editor.chain().focus().toggleStrike().run()}
+ class="p-2 rounded-md transition-colors hover:bg-zinc-100 dark:hover:bg-zinc-800 {editor.isActive('strike') ? 'bg-zinc-200 dark:bg-zinc-700' : '' }"
+ title="Strikethrough"
+ whileHover={{ scale: 1.1 }}
+ whileTap={{ scale: 0.9 }}
+ >
+
+
+
+ editor.chain().focus().toggleCode().run()}
+ class="p-2 rounded-md transition-colors hover:bg-zinc-100 dark:hover:bg-zinc-800 {editor.isActive('code') ? 'bg-zinc-200 dark:bg-zinc-700' : '' }"
+ title="Code"
+ whileHover={{ scale: 1.1 }}
+ whileTap={{ scale: 0.9 }}
+ >
+
+
+
+ {/if}
+
\ No newline at end of file
diff --git a/src/plugins/built-in/customMessageEditor/Editor/Plugins/Commands/CommandList.svelte b/src/plugins/built-in/customMessageEditor/Editor/Plugins/Commands/CommandList.svelte
new file mode 100644
index 00000000..d59818c5
--- /dev/null
+++ b/src/plugins/built-in/customMessageEditor/Editor/Plugins/Commands/CommandList.svelte
@@ -0,0 +1,172 @@
+
+
+
+
+{#if $slashVisible}
+
{}}
+ onclick={closeSlashMenu}
+ role="menu"
+ tabindex="-1">
+
+
+
+
Basic Blocks
+ {#each $slashItems as { title, subtitle, command }, i}
+
handleItemClick({ command })}
+ onkeydown={() => {}}
+ role="menuitem"
+ tabindex="-1"
+ bind:this={elements[i]}>
+
+ {@html getCommandIcon(title)}
+
+
+
+ {title}
+
+
+ {subtitle ? subtitle : ''}
+
+
+
+ {/each}
+
+{/if}
diff --git a/src/plugins/built-in/customMessageEditor/Editor/Plugins/Commands/command.ts b/src/plugins/built-in/customMessageEditor/Editor/Plugins/Commands/command.ts
new file mode 100644
index 00000000..4ae5ab34
--- /dev/null
+++ b/src/plugins/built-in/customMessageEditor/Editor/Plugins/Commands/command.ts
@@ -0,0 +1,26 @@
+import { Extension } from '@tiptap/core';
+import Suggestion from '@tiptap/suggestion';
+
+export default Extension.create({
+ name: 'slash',
+
+ addOptions() {
+ return {
+ suggestion: {
+ char: '/',
+ command: ({ editor, range, props }: any) => {
+ props.command({ editor, range });
+ },
+ },
+ };
+ },
+
+ addProseMirrorPlugins() {
+ return [
+ Suggestion({
+ editor: this.editor,
+ ...this.options.suggestion,
+ }),
+ ];
+ },
+});
diff --git a/src/plugins/built-in/customMessageEditor/Editor/Plugins/Commands/stores.ts b/src/plugins/built-in/customMessageEditor/Editor/Plugins/Commands/stores.ts
new file mode 100644
index 00000000..2138ba65
--- /dev/null
+++ b/src/plugins/built-in/customMessageEditor/Editor/Plugins/Commands/stores.ts
@@ -0,0 +1,46 @@
+import { writable } from 'svelte/store';
+import type { Writable } from 'svelte/store';
+
+type SlashItems = SlashItem[];
+
+type SlashItem = {
+ title: string;
+ subtitle: string;
+ command: ({ editor, range }: EditorProps) => void;
+};
+
+type Component = {
+ name: string;
+ description: string;
+ code: string;
+};
+
+type Components = Component[];
+
+type EditorProps = {
+ editor: any;
+ range: number | null;
+};
+
+type Location = {
+ x: number;
+ y: number;
+ height: number;
+};
+
+// For now we'll keep using stores until we can fully convert to runes in all components
+export const slashItems: Writable
= writable([]);
+export const slashVisible: Writable = writable(false);
+export const slashLocation: Writable = writable({
+ x: 0,
+ y: 0,
+ height: 0,
+});
+export const slashProps: Writable = writable({
+ editor: null,
+ range: null,
+});
+export const desktopMenu: Writable = writable(true);
+export const components: Writable = writable([]);
+export const editorWidth: Writable = writable(0);
+export const selectedIndex: Writable = writable(0);
diff --git a/src/plugins/built-in/customMessageEditor/Editor/Plugins/Commands/suggestion.ts b/src/plugins/built-in/customMessageEditor/Editor/Plugins/Commands/suggestion.ts
new file mode 100644
index 00000000..226abde5
--- /dev/null
+++ b/src/plugins/built-in/customMessageEditor/Editor/Plugins/Commands/suggestion.ts
@@ -0,0 +1,159 @@
+import { slashVisible, slashItems, slashLocation, slashProps, selectedIndex } from './stores';
+
+export default {
+ items: ({ query }: any) => {
+ return [
+ {
+ title: 'To Dos',
+ subtitle: 'Create a to do list with checkboxes',
+ command: ({ editor, range }: any) => {
+ editor.chain().focus().deleteRange(range).toggleTaskList().run();
+ },
+ },
+ {
+ title: 'Heading 1',
+ subtitle: 'BIG heading',
+ command: ({ editor, range }: any) => {
+ editor.chain().focus().deleteRange(range).setNode('heading', { level: 1 }).run();
+ },
+ },
+ {
+ title: 'Heading 2',
+ subtitle: 'Less Big heading',
+ command: ({ editor, range }: any) => {
+ editor.chain().focus().deleteRange(range).setNode('heading', { level: 2 }).run();
+ },
+ },
+ {
+ title: 'Heading 3',
+ subtitle: 'Medium big heading',
+ command: ({ editor, range }: any) => {
+ editor.chain().focus().deleteRange(range).setNode('heading', { level: 3 }).run();
+ },
+ },
+ {
+ title: 'Bullet List',
+ subtitle: 'Pew pew pew',
+ command: ({ editor, range }: any) => {
+ editor.commands.deleteRange(range);
+ editor.commands.toggleBulletList();
+ },
+ },
+ {
+ title: 'Numbered List',
+ subtitle: '1, 2, 3, 4...',
+ command: ({ editor, range }: any) => {
+ editor.commands.deleteRange(range);
+ editor.commands.toggleOrderedList();
+ },
+ },
+ {
+ title: 'Text',
+ subtitle: 'Just plain text paragraph',
+ command: ({ editor, range }: any) => {
+ editor.chain().focus().deleteRange(range).setNode('paragraph').run();
+ },
+ },
+ {
+ title: 'Quote',
+ subtitle: 'Capture important quotes',
+ command: ({ editor, range }: any) => {
+ editor.chain().focus().deleteRange(range).toggleBlockquote().run();
+ },
+ },
+ {
+ title: 'Code Block',
+ subtitle: 'Formatted code snippet',
+ command: ({ editor, range }: any) => {
+ editor.chain().focus().deleteRange(range).toggleCodeBlock().run();
+ },
+ },
+ {
+ title: 'Divider',
+ subtitle: 'Add a horizontal line',
+ command: ({ editor, range }: any) => {
+ editor.chain().focus().deleteRange(range).setHorizontalRule().run();
+ },
+ },
+ {
+ title: 'Bold Text',
+ subtitle: 'Make text bold',
+ command: ({ editor, range }: any) => {
+ editor.commands.deleteRange(range);
+ editor.commands.toggleBold();
+ },
+ },
+ {
+ title: 'Italic Text',
+ subtitle: 'Make text italic',
+ command: ({ editor, range }: any) => {
+ editor.commands.deleteRange(range);
+ editor.commands.toggleItalic();
+ },
+ },
+ {
+ title: 'Link',
+ subtitle: 'Add a web link',
+ command: ({ editor, range }: any) => {
+ const url = prompt('Enter the URL:');
+ if (url) {
+ editor
+ .chain()
+ .focus()
+ .deleteRange(range)
+ .setLink({ href: url })
+ .insertContent('Link text')
+ .run();
+ }
+ },
+ },
+ {
+ title: 'Inline Code',
+ subtitle: 'Inline code snippet',
+ command: ({ editor, range }: any) => {
+ editor.commands.deleteRange(range);
+ editor.commands.toggleCode();
+ },
+ },
+ ]
+ .filter((item) => item.title.toLowerCase().startsWith(query.toLowerCase()))
+ .slice(0, 10);
+ },
+
+ render: () => {
+ return {
+ onStart: (props: any) => {
+ let editor = props.editor;
+ let range = props.range;
+ let location = props.clientRect();
+ const editorRect = editor.view.dom.getBoundingClientRect();
+ slashProps.set({ editor, range });
+ slashVisible.set(true);
+ slashLocation.set({
+ x: location.x - editorRect.left,
+ y: location.y - editorRect.top + location.height / 2 + 4,
+ height: location.height,
+ });
+ slashItems.set(props.items);
+ selectedIndex.set(0);
+ },
+
+ onUpdate(props: any) {
+ slashItems.set(props.items);
+ selectedIndex.set(0);
+ },
+
+ onKeyDown(props: any) {
+ if (props.event.key === 'Escape') {
+ slashVisible.set(false);
+ return true;
+ }
+ },
+
+ onExit() {
+ slashVisible.set(false);
+ selectedIndex.set(0);
+ },
+ };
+ },
+};
diff --git a/src/plugins/built-in/customMessageEditor/Editor/TiptapStyles.css b/src/plugins/built-in/customMessageEditor/Editor/TiptapStyles.css
new file mode 100644
index 00000000..bbd90655
--- /dev/null
+++ b/src/plugins/built-in/customMessageEditor/Editor/TiptapStyles.css
@@ -0,0 +1,75 @@
+.ProseMirror {
+ position: relative;
+}
+
+.ProseMirror {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ white-space: break-spaces;
+ -webkit-font-variant-ligatures: none;
+ font-variant-ligatures: none;
+ font-feature-settings: "liga" 0; /* the above doesn't seem to work in Edge */
+}
+
+.ProseMirror [contenteditable="false"] {
+ white-space: normal;
+}
+
+.ProseMirror [contenteditable="false"] [contenteditable="true"] {
+ white-space: pre-wrap;
+}
+
+.ProseMirror pre {
+ white-space: pre-wrap;
+}
+
+img.ProseMirror-separator {
+ display: inline !important;
+ border: none !important;
+ margin: 0 !important;
+ width: 0 !important;
+ height: 0 !important;
+}
+
+.ProseMirror-gapcursor {
+ display: none;
+ pointer-events: none;
+ position: absolute;
+ margin: 0;
+}
+
+.ProseMirror-gapcursor:after {
+ content: "";
+ display: block;
+ position: absolute;
+ top: -2px;
+ width: 20px;
+ border-top: 1px solid black;
+ animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite;
+}
+
+@keyframes ProseMirror-cursor-blink {
+ to {
+ visibility: hidden;
+ }
+}
+
+.ProseMirror-hideselection *::selection {
+ background: transparent;
+}
+
+.ProseMirror-hideselection *::-moz-selection {
+ background: transparent;
+}
+
+.ProseMirror-hideselection * {
+ caret-color: transparent;
+}
+
+.ProseMirror-focused .ProseMirror-gapcursor {
+ display: block;
+}
+
+.tippy-box[data-animation=fade][data-state=hidden] {
+ opacity: 0
+}
\ No newline at end of file
diff --git a/src/plugins/built-in/customMessageEditor/Editor/userHTML.css b/src/plugins/built-in/customMessageEditor/Editor/userHTML.css
new file mode 100644
index 00000000..fd7326c0
--- /dev/null
+++ b/src/plugins/built-in/customMessageEditor/Editor/userHTML.css
@@ -0,0 +1,238 @@
+/* SEQTA Applied styles on DMs (applied to ensure consistency) */
+
+.editor-prose {
+ font-family: 'Roboto', sans-serif;
+ border: 0;
+ padding: 0 8px;
+ margin: 0;
+ line-height: 1.2;
+ /* Removed font size because drag and drop text content within the editor insert html span with font sizing */
+
+ font-size: 10pt;
+
+ /* Macro: Image */
+
+ /* Macro: Image gallery (display) */
+
+ /* Fake macro element from plugin "seqta-macro" */
+ img[data-macro],
+ a[data-macro] {
+ border: 2px dashed #ccc;
+ padding: 8px;
+ border-radius: 4px;
+ position: relative;
+ box-sizing: border-box;
+ }
+
+ img[data-macro].selected,
+ a[data-macro].selected {
+ border: 2px solid #204a87;
+ box-shadow: inset 0 0 4px #204a87;
+ }
+
+ img[data-macro='Resource'] {
+ background-image: repeating-linear-gradient(
+ -45deg,
+ rgba(0, 0, 0, 0),
+ rgba(0, 0, 0, 0) 12px,
+ rgba(0, 0, 0, 0.05) 12px,
+ rgba(0, 0, 0, 0.05) 24px
+ );
+ }
+
+ img[data-macro='Embed'] {
+ background-image: repeating-linear-gradient(
+ 45deg,
+ rgba(0, 0, 0, 0),
+ rgba(0, 0, 0, 0) 12px,
+ rgba(0, 0, 0, 0.05) 12px,
+ rgba(0, 0, 0, 0.05) 24px
+ );
+ }
+
+ img[data-macro='Embed'][data-full] {
+ width: 100%;
+ }
+
+ img[data-macro='Gallery'] {
+ display: block;
+ margin: 0 auto;
+ max-width: 100%;
+ padding: 0;
+ }
+
+ /* Direqt message-specific styling */
+ blockquote.forward {
+ margin: 0;
+ background: rgba(0, 0, 0, 0.05);
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ }
+
+ blockquote.forward > .preamble {
+ background: rgba(255, 255, 255, 0.1);
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+ padding: 8px;
+ }
+
+ blockquote.forward > .preamble > .date > .label,
+ blockquote.forward > .preamble > .sender > .label {
+ color: rgba(0, 0, 0, 0.7);
+ }
+
+ blockquote.forward > .preamble > .date > .value,
+ blockquote.forward > .preamble > .sender > .value {
+ color: rgba(0, 0, 0, 0.9);
+ }
+
+ blockquote.forward > .body {
+ padding: 8px;
+ }
+
+ /** Assessment display **/
+
+ .assessmentWrapper {
+ position: relative;
+ display: inline-block;
+ bottom: -15px;
+ }
+
+ .macro-assessment {
+ padding: 8px 16px;
+ margin: 0 8px;
+ border: 4px solid rgba(0, 0, 0, 0.25);
+ display: inline-block;
+ width: 256px;
+ background-color: #fff;
+ overflow: hidden;
+ text-shadow: none;
+ position: relative;
+ word-wrap: break-word;
+ min-height: 30px;
+ }
+
+ .macro-assessment > .title {
+ font-size: 150%;
+ max-width: 230px;
+ }
+
+ .macro-assessment > .due > span.weight {
+ padding-left: 24px;
+ }
+
+ .macro-assessment > .due > span.marked {
+ padding-left: 24px;
+ font-weight: bold;
+ }
+
+ .macro-assessment > .hidden,
+ .macro-assessment > .deleted {
+ font-style: italic;
+ max-width: 230px;
+ }
+
+ /** Syllabus display **/
+
+ .macro-syllabus {
+ padding: 8px;
+ margin: 0 8px;
+ border: 4px solid #eee;
+ display: inline-block;
+ max-width: 200px;
+ background-color: #fff;
+ overflow: hidden;
+ color: #444;
+ text-shadow: none;
+ position: relative;
+ bottom: -15px;
+ }
+
+ .macro-syllabus > .label {
+ font-weight: bold;
+ }
+
+ .macro-syllabus > .extra {
+ font-style: italic;
+ }
+
+ .macro-syllabus > .meta {
+ text-transform: uppercase;
+ font-size: var(--small-text);
+ color: #999;
+ }
+
+ /* Drop-down menu for plugins like "seqta-macro" */
+ .cke_panel_block > h1 {
+ display: none;
+ }
+
+ .cke_panel_block > .cke_panel_list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ }
+
+ .cke_panel_block > .cke_panel_list > li {
+ color: #888;
+ margin: 0;
+ cursor: pointer;
+ }
+
+ .cke_panel_block > .cke_panel_list > li:hover {
+ color: white;
+ background: #1b315e;
+ }
+
+ .cke_panel_block > .cke_panel_list > li > a {
+ display: block;
+ color: inherit;
+ text-decoration: inherit;
+ text-transform: uppercase;
+ font-size: 90%;
+ padding: 8px; /* Padding on the , not the , so our click target is full size. */
+ text-shadow: none;
+ }
+
+ .cke_panel_block > .cke_panel_list > li > a > p,
+ .cke_panel_block > .cke_panel_list > li > a > h1,
+ .cke_panel_block > .cke_panel_list > li > a > h2,
+ .cke_panel_block > .cke_panel_list > li > a > h3,
+ .cke_panel_block > .cke_panel_list > li > a > pre {
+ margin: 0;
+ color: inherit;
+ padding: 0;
+ }
+
+ .moodleFrame > .userHTML {
+ width: 100%;
+ height: 600px;
+ margin: 16px 0 16px 0;
+ }
+
+ .application.restricted {
+ display: block;
+ max-width: 320px;
+ margin: 32px auto;
+ border: 1px dashed #ccc;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
+ padding: 24px;
+ background: #f8f8f8;
+ background-image: -webkit-linear-gradient(315deg, #fff, #f8f8f8);
+ background-image: linear-gradient(135deg, #fff, #f8f8f8);
+ border-radius: 8px;
+ box-sizing: border-box;
+ }
+
+ .application.restricted > .title {
+ margin: 0;
+ padding: 0;
+ font-size: 100%;
+ font-weight: bold;
+ color: #666;
+ }
+
+ .application.restricted > .message {
+ margin: 0;
+ padding: 0;
+ font-size: var(--small-text);
+ }
+}
diff --git a/src/plugins/built-in/customMessageEditor/index.ts b/src/plugins/built-in/customMessageEditor/index.ts
new file mode 100644
index 00000000..d496636d
--- /dev/null
+++ b/src/plugins/built-in/customMessageEditor/index.ts
@@ -0,0 +1,236 @@
+import type { Plugin } from "@/plugins/core/types";
+import { BasePlugin } from "@/plugins/core/settings";
+import { defineSettings } from "@/plugins/core/settingsHelpers";
+import { waitForElm } from "@/seqta/utils/waitForElm";
+import renderSvelte from "@/interface/main";
+import BetterEditor from "./BetterEditor.svelte";
+import { unmount } from "svelte";
+
+const settings = defineSettings({});
+
+class CustomMessageEditorPlugin extends BasePlugin {}
+
+const settingsInstance = new CustomMessageEditorPlugin();
+
+const customMessageEditorPlugin: Plugin = {
+ id: "custom-message-editor",
+ name: "Custom Message Editor",
+ description: "Enhanced message editor with better editing capabilities",
+ version: "1.0.0",
+ settings: settingsInstance.settings,
+ defaultEnabled: true,
+
+ run: async (api) => {
+ let currentShadowContainer: HTMLElement | null = null;
+ let currentSvelteApp: any = null;
+ let currentEditorId: string | null = null;
+ let lastCKEditorContent: string = "";
+
+ const cleanup = (resetEditorId = true) => {
+ if (currentSvelteApp) {
+ unmount(currentSvelteApp);
+ currentSvelteApp = null;
+ }
+ if (currentShadowContainer) {
+ currentShadowContainer.remove();
+ currentShadowContainer = null;
+ }
+ if (resetEditorId) {
+ currentEditorId = null;
+ }
+ };
+
+ const handleEditorChange = (value: string) => {
+ if (currentEditorId) {
+ window.postMessage(
+ {
+ type: "ckeditorSetData",
+ editorId: currentEditorId,
+ content: value,
+ },
+ "*",
+ );
+ }
+ };
+
+ const getCKEditorContent = () => {
+ if (currentEditorId) {
+ window.postMessage(
+ {
+ type: "ckeditorGetData",
+ editorId: currentEditorId,
+ },
+ "*",
+ );
+ }
+ };
+
+ const messageListener = (event: MessageEvent) => {
+ if (event.data.type === "ckeditorGetDataResponse") {
+ lastCKEditorContent = event.data.data;
+ console.log("Retrieved CKEditor content:", lastCKEditorContent);
+ }
+ };
+
+ window.addEventListener("message", messageListener);
+
+ const injectBetterEditorButton = async (composer: Element) => {
+ try {
+ const pillbox = await waitForElm(
+ ".coneqtMessage.composer .footer .pillbox",
+ true,
+ 100,
+ 50,
+ );
+
+ if (!pillbox) {
+ console.error("Could not find pillbox element");
+ return;
+ }
+
+ if (pillbox.querySelector(".better-editor-btn")) {
+ return;
+ }
+
+ const betterEditorBtn = document.createElement("button");
+ betterEditorBtn.type = "button";
+ betterEditorBtn.className = "notLast editorMode better-editor-btn";
+ betterEditorBtn.textContent = "Better Editor";
+ betterEditorBtn.setAttribute("data-key", "better");
+
+ const htmlEditorBtn = pillbox.querySelector(
+ 'button[data-key="html"]',
+ ) as HTMLButtonElement;
+ if (!htmlEditorBtn) {
+ console.error("Could not find HTML editor button");
+ return;
+ }
+
+ pillbox.insertBefore(betterEditorBtn, htmlEditorBtn);
+
+ betterEditorBtn.addEventListener("click", async () => {
+ const simpleEditorBtn = pillbox.querySelector(
+ 'button[data-key="content"]',
+ ) as HTMLButtonElement;
+ if (simpleEditorBtn) {
+ simpleEditorBtn.click();
+ }
+
+ pillbox.querySelectorAll(".editorMode").forEach((btn) => {
+ btn.classList.remove("depressed");
+ });
+ if (simpleEditorBtn) {
+ simpleEditorBtn.classList.add("depressed");
+ }
+
+ const wrapper = composer.querySelector(
+ ".prime .body .formattedText .wrapper",
+ );
+ const ckeElement = wrapper?.querySelector(".cke");
+
+ if (!wrapper || !ckeElement) {
+ console.error("Could not find wrapper or CKE elements");
+ return;
+ }
+
+ if (ckeElement.id) {
+ const ckeMatch = ckeElement.id.match(/^cke_(.+)$/);
+ if (ckeMatch) {
+ currentEditorId = ckeMatch[1];
+ console.log("Found CKEditor ID:", currentEditorId);
+ }
+ }
+
+ let initialContent = "";
+
+ if (currentEditorId) {
+ window.postMessage(
+ {
+ type: "ckeditorGetData",
+ editorId: currentEditorId,
+ },
+ "*",
+ );
+
+ initialContent = await new Promise((resolve) => {
+ const timeout = setTimeout(() => resolve(""), 1000);
+
+ const responseListener = (event: MessageEvent) => {
+ if (event.data.type === "ckeditorGetDataResponse") {
+ clearTimeout(timeout);
+ window.removeEventListener("message", responseListener);
+ resolve(event.data.data || "");
+ }
+ };
+
+ window.addEventListener("message", responseListener);
+ });
+ }
+
+ (ckeElement as HTMLElement).style.display = "none";
+
+ cleanup(false);
+
+ const shadowContainer = document.createElement("div");
+ shadowContainer.className = "better-editor-container";
+ shadowContainer.style.cssText =
+ "width: 100%; height: 100%; min-height: 200px; overflow-y: scroll; background: var(--background-primary); border-radius: 16px; padding: 4px;";
+
+ const shadowRoot = shadowContainer.attachShadow({ mode: "open" });
+
+ currentSvelteApp = renderSvelte(BetterEditor, shadowRoot, {
+ initialContent,
+ onchange: handleEditorChange,
+ });
+
+ wrapper.appendChild(shadowContainer);
+ currentShadowContainer = shadowContainer;
+
+ pillbox.querySelectorAll(".editorMode").forEach((btn) => {
+ btn.classList.remove("depressed");
+ });
+ betterEditorBtn.classList.add("depressed");
+ });
+
+ pillbox
+ .querySelectorAll(".editorMode:not(.better-editor-btn)")
+ .forEach((btn) => {
+ btn.addEventListener("click", () => {
+ getCKEditorContent();
+
+ cleanup(false);
+
+ const wrapper = composer.querySelector(
+ ".prime .body .formattedText .wrapper",
+ );
+ const ckeElement = wrapper?.querySelector(".cke");
+ if (ckeElement) {
+ (ckeElement as HTMLElement).style.display = "";
+ }
+ });
+ });
+ } catch (error) {
+ console.error("Error injecting Better Editor button:", error);
+ }
+ };
+
+ const { unregister } = api.seqta.onMount(".uiSlidePane", (slidePane) => {
+ console.log("Found slide pane, checking for message composer");
+ const messageComposer = slidePane.querySelector(
+ ".coneqtMessage.composer",
+ );
+ if (messageComposer) {
+ console.log("Found message composer, injecting Better Editor button");
+ injectBetterEditorButton(messageComposer);
+ }
+ });
+
+ return () => {
+ cleanup();
+ unregister();
+ window.removeEventListener("message", messageListener);
+ };
+ },
+};
+
+export default customMessageEditorPlugin;
diff --git a/src/plugins/index.ts b/src/plugins/index.ts
index 1c06d785..9e7cbc87 100644
--- a/src/plugins/index.ts
+++ b/src/plugins/index.ts
@@ -9,6 +9,7 @@ import assessmentsAveragePlugin from "./built-in/assessmentsAverage";
import globalSearchPlugin from "./built-in/globalSearch/src/core";
import profilePicturePlugin from "./built-in/profilePicture";
import assessmentsOverviewPlugin from "./built-in/assessmentsOverview";
+import customMessageEditorPlugin from "./built-in/customMessageEditor";
//import testPlugin from './built-in/test';
// Initialize plugin manager
@@ -23,6 +24,7 @@ pluginManager.registerPlugin(timetablePlugin);
pluginManager.registerPlugin(globalSearchPlugin);
pluginManager.registerPlugin(profilePicturePlugin);
pluginManager.registerPlugin(assessmentsOverviewPlugin);
+pluginManager.registerPlugin(customMessageEditorPlugin);
//pluginManager.registerPlugin(testPlugin);
export { init as Monofile } from "./monofile";