refactor(ai): extract assistant into an official module (#749)

* feat(modules): expose host data boundaries

* feat(modules): add frontend extension surfaces

* refactor(ai): remove assistant from core

* chore(ai): prepare the module extraction

* fix(modules): load extension styles after the host bundle

* chore(modules): lock SDK 3.3.0
This commit is contained in:
Darko Gjorgjijoski
2026-08-05 22:10:21 +02:00
committed by GitHub
parent 99ae7def75
commit 0b9ae9ea00
111 changed files with 1137 additions and 8952 deletions
@@ -37,6 +37,10 @@
{{ button.text }}
</span>
</button>
<ExtensionSlot
name="rich-editor-toolbar-actions"
:context="editorContext"
/>
</div>
</BaseDropdown>
</div>
@@ -58,6 +62,10 @@
{{ button.text }}
</span>
</button>
<ExtensionSlot
name="rich-editor-toolbar-actions"
:context="editorContext"
/>
</div>
</div>
<editor-content
@@ -94,11 +102,10 @@ import {
Bars3BottomRightIcon,
Bars3Icon,
LinkIcon,
SparklesIcon,
} from '@heroicons/vue/24/solid'
import { ContentPlaceholder, ContentPlaceholderBox } from '../layout'
import { useGlobalStore } from '@/scripts/stores/global.store'
import { useModalStore } from '@/scripts/stores/modal.store'
import ExtensionSlot from '@/scripts/extensions/ExtensionSlot.vue'
import type { RichEditorContext } from '@/scripts/extensions/types'
interface EditorButton {
name: string
@@ -170,39 +177,21 @@ const editorButtons = ref<EditorButton[]>([
},
])
// AI text-generation button — shown only when the feature is enabled
// for the current company. The flag is set once at bootstrap time so a
// one-shot push is fine; no reactivity needed.
const globalStore = useGlobalStore()
const modalStore = useModalStore()
if (globalStore.ai?.enabled && globalStore.ai?.text_generation_enabled) {
editorButtons.value.push({
name: 'aiGenerate',
icon: markRaw(SparklesIcon) as Component,
action: () => {
modalStore.openModal({
componentName: 'AiTextGenerationModal',
title: 'AI Text Generation',
size: 'md',
data: {
currentContent: editor.value?.getHTML() ?? '',
onInsert: (text: string) => {
editor.value?.chain().focus().insertContent(text).run()
},
onReplace: (text: string) => {
editor.value?.chain().focus().selectAll().deleteSelection().insertContent(text).run()
},
},
})
},
})
const editorContext: RichEditorContext = {
getHtml: () => editor.value?.getHTML() ?? '',
insertContent: (content: string) => {
editor.value?.chain().focus().insertContent(content).run()
},
replaceContent: (content: string) => {
editor.value?.chain().focus().selectAll().deleteSelection().insertContent(content).run()
},
}
watch(
() => props.modelValue,
(newValue: string) => {
if (editor.value && newValue !== editor.value.getHTML()) {
editor.value.commands.setContent(newValue, false)
editor.value.commands.setContent(newValue, { emitUpdate: false })
}
}
)