diff --git a/CHANGELOG.md b/CHANGELOG.md
index 68efd5a592bd93b6b9f63be3d1f265487ffc5b41..fbdde680b89e76f9507725101335ff0739a6a3b2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,8 +7,14 @@
## [Unreleased]
+### Changed
+
+- 更新AI行内聊天上下文菜单样式
+- 更新html全屏样式
+
### Added
+- 新增markdown支持AI行内聊天
- 新增markdown支持手动编辑模式
- 新增重复器表格样式2组件
- 新增代码编辑器支持AI行内聊天
diff --git a/src/editor/html/wang-editor/wang-editor.scss b/src/editor/html/wang-editor/wang-editor.scss
index 2b4e41eae362a4f310eaad45266de7929141580f..10e6aa67193eb9671a1e9ce89e951e0e082ba704 100644
--- a/src/editor/html/wang-editor/wang-editor.scss
+++ b/src/editor/html/wang-editor/wang-editor.scss
@@ -84,6 +84,18 @@ $html: (
width: 100%;
}
}
+
+ @include when('enable-edit'){
+ // 整体处于允许编辑的情况下,默认的工具栏样式变为聚焦时的样式
+ .w-e-bar-item>button{
+ color: var(--w-e-toolbar-color);
+ cursor: pointer;
+
+ svg{
+ fill: var(--w-e-toolbar-color);
+ }
+ }
+ }
}
@include b('html-editor-readonly') {
@@ -96,6 +108,21 @@ $html: (
}
}
+@include b('html-toolbar'){
+ .w-e-toolbar{
+ background-color: getCssVar(color, bg, 0);
+ border: 1px solid getCssVar(color, border);
+ border-bottom: none;
+ box-shadow: none;
+
+ .w-e-bar-item>button{
+ &:hover{
+ background-color: getCssVar(color, bg, 0);
+ }
+ }
+ }
+}
+
@include b('html-footer') {
display: flex;
align-items: center;
@@ -187,28 +214,50 @@ $html: (
@include b('html-dialog-full-screen') {
height: 80%;
+ .el-dialog__header{
+ display: none;
+ }
+
+ .el-dialog__body{
+ height: 100%;
+ padding-top: 0;
+ }
+
+ @include when('editing'){
+ .el-dialog__body{
+ padding-bottom: 0;
+ }
+ }
+
@include b('html') {
- padding: 0 getCssVar('spacing', 'extra-loose');
+ padding: 0;
--w-e-toolbar-bg-color: #{getCssVar(color, bg, 0)};
}
@include b('html-custom-toolbar') {
height: 56px;
+ padding-right: 0;
}
@include b('html-content') {
- height: calc(100% - 124px);
+ height: calc(100% - 56px);
@include b('html-editor') {
height: 100% !important;
}
+
+ @include when('editing'){
+ height: calc(100% - 128px);
+ }
}
+
}
@include b('html-footer-dialog') {
height: 68px;
margin-top: 0;
+ margin-right: 0;
}
// 表情元素
diff --git a/src/editor/html/wang-editor/wang-editor.tsx b/src/editor/html/wang-editor/wang-editor.tsx
index 970f79bd4c5774de55e99d7c1894475f19add0d2..3c2ae8ccac0894cd43a7a3ef7ac9f1653ee460f1 100644
--- a/src/editor/html/wang-editor/wang-editor.tsx
+++ b/src/editor/html/wang-editor/wang-editor.tsx
@@ -966,7 +966,11 @@ const IBizHtml = defineComponent({
// 绘制编辑器内容
const renderEditorContent = () => {
return (
-
+
{this.renderHeaserToolbar()}
@@ -1053,7 +1058,10 @@ const IBizHtml = defineComponent({
v-model={this.isFullScreen}
width='80%'
top='10vh'
- class={this.ns.b('dialog-full-screen')}
+ class={[
+ this.ns.b('dialog-full-screen'),
+ this.ns.is('editing', !this.readonlyState),
+ ]}
onClose={() => this.changeFullScreenState()}
>
+
+
+
+
+ `,
+ },
+ onClick: (_selection: string, _menukey: string, event: MouseEvent) => {
+ const startPos = c.mdeditor?.editor.editor.getCursor('start');
+ const endPos = c.mdeditor?.editor.editor.getCursor('end');
+ c.setCursorPos(startPos, endPos);
+ event.stopPropagation();
+ event.preventDefault();
+ nextTick(() => {
+ if (c.mdeditor?.bubble) {
+ c.mdeditor.bubble.visible = true;
+ const selectionPosition =
+ c.mdeditor.bubble.bubbleDom.getBoundingClientRect();
+ if (
+ !selectionPosition ||
+ !selectionPosition.left ||
+ !selectionPosition.top
+ )
+ return;
+ const items: MenuItem[] = ibiz.inLineAIUtil.calcContextMenus(
+ c.deACMode,
+ (tag: string) => {
+ c.doInLineAIUIAction(tag, c.model.appId);
+ },
+ );
+ if (items.length === 0) return;
+ ibiz.inLineAIUtil.showContextMenus(
+ selectionPosition.left,
+ selectionPosition.top + 60,
+ items,
+ );
+ }
+ });
+ },
+ });
+ return [AIMenu];
+}
diff --git a/src/editor/markdown/ibiz-markdown-editor/ibiz-markdown-editor.tsx b/src/editor/markdown/ibiz-markdown-editor/ibiz-markdown-editor.tsx
index 7ad9133b7d5bb5b64699e2e5151dc09111f78b5b..0604dac9f0afa33e6fd9b6160fd68147a3df08b3 100644
--- a/src/editor/markdown/ibiz-markdown-editor/ibiz-markdown-editor.tsx
+++ b/src/editor/markdown/ibiz-markdown-editor/ibiz-markdown-editor.tsx
@@ -19,6 +19,7 @@ import { createUUID } from 'qx-util';
import Cherry from 'cherry-markdown';
import { MarkDownEditorController } from '../markdown-editor.controller';
import './ibiz-markdown-editor.scss';
+import { initCustomMenu } from './custom-menu';
/**
* Markdown编辑框
@@ -52,6 +53,8 @@ const IBizMarkDown: any = defineComponent({
const id = createUUID();
+ const [AIMenu] = initCustomMenu(c);
+
// 请求头
const uploadHeaders = ibiz.util.file.getUploadHeaders();
const headers: Ref
= ref({ ...uploadHeaders });
@@ -317,6 +320,7 @@ const IBizMarkDown: any = defineComponent({
// 关闭全屏
if (isFullscreen()) {
closeFullscreen();
+ isFullScreen.value = false;
}
}
};
@@ -326,6 +330,20 @@ const IBizMarkDown: any = defineComponent({
defaultModel.value = 'previewOnly';
}
nextTick(() => {
+ const bubble = [
+ 'bold',
+ 'italic',
+ 'underline',
+ 'strikethrough',
+ 'sub',
+ 'sup',
+ '|',
+ 'size',
+ 'color',
+ ];
+ if (c.editorParams.ac && c.deACMode) {
+ bubble.unshift('AI');
+ }
editor = new Cherry({
id,
value: currentVal.value,
@@ -386,17 +404,7 @@ const IBizMarkDown: any = defineComponent({
'settings',
'togglePreview',
],
- bubble: [
- 'bold',
- 'italic',
- 'underline',
- 'strikethrough',
- 'sub',
- 'sup',
- '|',
- 'size',
- 'color',
- ],
+ bubble,
float: [
'h1',
'h2',
@@ -407,7 +415,9 @@ const IBizMarkDown: any = defineComponent({
'quickTable',
'code',
],
- customMenu: [],
+ customMenu: {
+ AI: AIMenu,
+ },
// 定义侧边栏,默认为空
sidebar: ['theme', 'copy'],
// 定义顶部右侧工具栏,默认为空
@@ -456,6 +466,7 @@ const IBizMarkDown: any = defineComponent({
'.cherry-toolbar>.toolbar-right',
);
parentElement?.appendChild(span);
+ c.setMDEditor(editor);
});
};
@@ -542,15 +553,23 @@ const IBizMarkDown: any = defineComponent({
onClick={onEnableEdit}
title={ibiz.i18n.t('editor.markdown.edit')}
>
-
+
)}
-
);
diff --git a/src/editor/markdown/markdown-editor.controller.ts b/src/editor/markdown/markdown-editor.controller.ts
index 2f99b7bb5b6929f795aae9d9d8df490101deca5c..fa7509996322c760e13c0d6ddc3304aabed18f5b 100644
--- a/src/editor/markdown/markdown-editor.controller.ts
+++ b/src/editor/markdown/markdown-editor.controller.ts
@@ -2,6 +2,9 @@ import { RuntimeModelError } from '@ibiz-template/core';
import {
EditorController,
IAppDEService,
+ IInLineAIEditor,
+ IInLineAiChatOptions,
+ UIActionUtil,
getDeACMode,
} from '@ibiz-template/runtime';
import { IAppDEACMode, IDEACModeDataItem, IMarkdown } from '@ibiz/model-core';
@@ -13,7 +16,10 @@ import { IAppDEACMode, IDEACModeDataItem, IMarkdown } from '@ibiz/model-core';
* @class MarkDownEditorController
* @extends {EditorController}
*/
-export class MarkDownEditorController extends EditorController {
+export class MarkDownEditorController
+ extends EditorController
+ implements IInLineAIEditor
+{
/**
* 上传参数
*/
@@ -87,6 +93,22 @@ export class MarkDownEditorController extends EditorController {
*/
chatCompletion: boolean = false;
+ /**
+ * 编辑器实例
+ *
+ * @type {IData}
+ * @memberof MarkDownEditorController
+ */
+ mdeditor: IData | null = null;
+
+ /**
+ * 选区位置缓存
+ *
+ * @type {(IData | null)}
+ * @memberof MarkDownEditorController
+ */
+ selectionAreaPosition: IData | null = null;
+
protected async onInit(): Promise {
await super.onInit();
@@ -161,4 +183,109 @@ export class MarkDownEditorController extends EditorController {
}
}
}
+
+ /**
+ * 设置编辑器实例
+ *
+ * @param {IData} mdeditor
+ * @memberof MarkDownEditorController
+ */
+ public setMDEditor(mdeditor: IData): void {
+ this.mdeditor = mdeditor;
+ }
+
+ /**
+ * 设置保存当前选区位置
+ *
+ * @param {IData} start
+ * @param {IData} end
+ * @memberof MarkDownEditorController
+ */
+ setCursorPos(start: IData, end: IData): void {
+ this.selectionAreaPosition = { start, end };
+ }
+
+ /**
+ * 获取选中文本
+ *
+ * @return {*} {string}
+ * @memberof MarkDownEditorController
+ */
+ public getSelectionText(): string {
+ return this.mdeditor?.editor.editor.getSelection();
+ }
+
+ /**
+ * 插入文本
+ *
+ * @param {string} text
+ * @memberof MarkDownEditorController
+ */
+ insertText(text: string): void {
+ this.mdeditor?.insert(text);
+ }
+
+ /**
+ * 替换选中文本
+ *
+ * @param {string} text
+ * @memberof MarkDownEditorController
+ */
+ replaceSelectionText(text: string): void {
+ this.mdeditor?.editor.editor.replaceSelection(text);
+ }
+
+ /**
+ * 恢复选区
+ *
+ * @memberof MarkDownEditorController
+ */
+ restoreSelection(): void {
+ this.mdeditor?.editor.editor.setSelection(
+ this.selectionAreaPosition?.start,
+ this.selectionAreaPosition?.end,
+ );
+ }
+
+ /**
+ * 获取内联AI参数
+ *
+ * @return {*} {IData}
+ * @memberof MarkDownEditorController
+ */
+ getInLineAiChatOptions(): IInLineAiChatOptions {
+ const editorRect = this.mdeditor?.wrapperDom.getBoundingClientRect();
+ return {
+ left: editorRect?.left,
+ top: editorRect?.top,
+ width: editorRect?.width,
+ };
+ }
+
+ /**
+ * 执行内联AIUI操作
+ *
+ * @param {string} _uiAction
+ * @param {string} _appId
+ * @return {*} {Promise}
+ * @memberof MarkDownEditorController
+ */
+ async doInLineAIUIAction(uiActionId: string, appId: string): Promise {
+ const eventArgs = this.ctrl.getEventArgs();
+ eventArgs.params.editor = this;
+ // 编辑器参数srfaiappendcurdata,是否传入对象参数,用于历史查询传参
+ if (
+ this.editorParams.srfaiappendcurdata &&
+ this.editorParams.srfaiappendcurdata === 'true'
+ ) {
+ eventArgs.context.srfaiappendcurdata = true;
+ }
+ await UIActionUtil.exec(
+ uiActionId!,
+ {
+ ...eventArgs,
+ },
+ appId,
+ );
+ }
}
diff --git a/src/util/inline-ai-util/inline-ai-textarea/inline-ai-textarea.scss b/src/util/inline-ai-util/inline-ai-textarea/inline-ai-textarea.scss
index e13df9738a6dd56831c7f2ac6a981b61d0f8e170..64fc5a39ceab4ebf21e84a49925cb651dfbf9dde 100644
--- a/src/util/inline-ai-util/inline-ai-textarea/inline-ai-textarea.scss
+++ b/src/util/inline-ai-util/inline-ai-textarea/inline-ai-textarea.scss
@@ -1,5 +1,27 @@
+$inline-ai-context-menu: (
+ color-bg: getCssVar(color,bg,2),
+ color-text: getCssVar(color,text,0),
+ color-bg-active: getCssVar(color,fill,0),
+);
@include b(inline-ai-container-context-menu) {
- color: red;
+ @include set-component-css-var('inline-ai-context-menu',$inline-ai-context-menu);
+
+ --mx-menu-text: #{getCssVar(inline-ai-context-menu,color-text)};
+ --mx-menu-backgroud: #{getCssVar(inline-ai-context-menu,color-bg)};
+ --mx-menu-hover-backgroud: #{getCssVar(inline-ai-context-menu,color-bg-active)};
+ --mx-menu-hover-text: #{getCssVar(inline-ai-context-menu,color-text)};
+ --mx-menu-open-text: #{getCssVar(inline-ai-context-menu,color-text)};
+ --mx-menu-open-backgroud:#{getCssVar(inline-ai-context-menu,color-bg-active)};
+ --mx-menu-open-hover-text:#{getCssVar(inline-ai-context-menu,color-text)};
+ --mx-menu-open-hover-backgroud:#{getCssVar(inline-ai-context-menu,color-bg-active)};
+
+ .scroll-content{
+ pointer-events: unset;
+ }
+
+ .mx-context-menu{
+ cursor: pointer;
+ }
}
@include b(inline-ai-textarea-container) {