diff --git a/src/components/input/editor/CommandsList.vue b/src/components/input/editor/CommandsList.vue
new file mode 100644
index 000000000..a5f5c8048
--- /dev/null
+++ b/src/components/input/editor/CommandsList.vue
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+ No result
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/input/editor/commands.js b/src/components/input/editor/commands.js
new file mode 100644
index 000000000..a16f097c9
--- /dev/null
+++ b/src/components/input/editor/commands.js
@@ -0,0 +1,26 @@
+import { Extension } from '@tiptap/core'
+import Suggestion from '@tiptap/suggestion'
+
+export default Extension.create({
+ name: 'commands',
+
+ addOptions() {
+ return {
+ suggestion: {
+ char: '/',
+ command: ({ editor, range, props }) => {
+ props.command({ editor, range })
+ },
+ },
+ }
+ },
+
+ addProseMirrorPlugins() {
+ return [
+ Suggestion({
+ editor: this.editor,
+ ...this.options.suggestion,
+ }),
+ ]
+ },
+})
\ No newline at end of file
diff --git a/src/components/input/editor/suggestion.js b/src/components/input/editor/suggestion.js
new file mode 100644
index 000000000..58c501023
--- /dev/null
+++ b/src/components/input/editor/suggestion.js
@@ -0,0 +1,113 @@
+import { VueRenderer } from '@tiptap/vue-3'
+import tippy from 'tippy.js'
+
+import CommandsList from './CommandsList.vue'
+
+export default {
+ items: ({ query }) => {
+ return [
+ {
+ title: 'H1',
+ command: ({ editor, range }) => {
+ editor
+ .chain()
+ .focus()
+ .deleteRange(range)
+ .setNode('heading', { level: 1 })
+ .run()
+ },
+ },
+ {
+ title: 'H2',
+ command: ({ editor, range }) => {
+ editor
+ .chain()
+ .focus()
+ .deleteRange(range)
+ .setNode('heading', { level: 2 })
+ .run()
+ },
+ },
+ {
+ title: 'bold',
+ command: ({ editor, range }) => {
+ editor
+ .chain()
+ .focus()
+ .deleteRange(range)
+ .setMark('bold')
+ .run()
+ },
+ },
+ {
+ title: 'italic',
+ command: ({ editor, range }) => {
+ editor
+ .chain()
+ .focus()
+ .deleteRange(range)
+ .setMark('italic')
+ .run()
+ },
+ },
+ ].filter(item => item.title.toLowerCase().startsWith(query.toLowerCase())).slice(0, 10)
+ },
+
+ render: () => {
+ let component
+ let popup
+
+ return {
+ onStart: props => {
+ component = new VueRenderer(CommandsList, {
+ // using vue 2:
+ // parent: this,
+ // propsData: props,
+ props,
+ editor: props.editor,
+ })
+
+ if (!props.clientRect) {
+ return
+ }
+
+ popup = tippy('body', {
+ getReferenceClientRect: props.clientRect,
+ appendTo: () => document.body,
+ content: component.element,
+ showOnCreate: true,
+ interactive: true,
+ trigger: 'manual',
+ placement: 'bottom-start',
+ })
+ },
+
+ onUpdate(props) {
+ component.updateProps(props)
+
+ if (!props.clientRect) {
+ return
+ }
+
+ popup[0].setProps({
+ getReferenceClientRect: props.clientRect,
+ })
+ },
+
+ onKeyDown(props) {
+ if (props.event.key === 'Escape') {
+ popup[0].hide()
+
+ return true
+ }
+
+ return component.ref?.onKeyDown(props)
+ },
+
+ onExit() {
+ popup[0].destroy()
+ component.destroy()
+ },
+ }
+ },
+}
\ No newline at end of file