diff --git a/CHANGELOG.md b/CHANGELOG.md index 82fd2fdcdc603e40e13ddff8b359595ee7cda88a..13d435c81b411f79de712698a39c67064d3cb8b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ ## [Unreleased] +### Added + +- 新增代码编辑器获取编辑器元素及主题方法,并更新位置计算逻辑的注释 + +### Changed + +- 优化代码编辑器插入文本方法,将文本以段落的格式插入到光标位置 +- 优化代码编辑器样式,适配编辑器的dark主题 + ## [0.7.41-alpha.38] - 2025-11-19 ### Added diff --git a/src/editor/code/code-editor.controller.ts b/src/editor/code/code-editor.controller.ts index b6b32d5e8d44f99f03dc2e042cf39d4a4b163b7f..1a1ccb549e9d17fc11b3834b449532b217cfd731 100644 --- a/src/editor/code/code-editor.controller.ts +++ b/src/editor/code/code-editor.controller.ts @@ -129,36 +129,46 @@ export class CodeEditorController * @param {string} text 文本 */ insertText(text: string): void { - const model = this.editor?.getModel(); - if (!model || !this.monaco) return; + if (!this.editor || !this.monaco) { + throw new RuntimeError(ibiz.i18n.t('editor.code.editorNotInit')); + } + + // 取选中结束位置作为插入点 + const selections = this.editor!.getSelections(); + // 无光标时不执行插入 + if (!selections || selections.length === 0) return; + + const activeSelection = selections[selections.length - 1]; + const insertLine = activeSelection.positionLineNumber; // 活跃光标的行号 + const insertColumn = activeSelection.positionColumn; // 活跃光标的列号 - const lastLineNumber = model.getLineCount(); - const lastLineContent = model.getLineContent(lastLineNumber); - const lastColumn = lastLineContent.length + 1; + // 以段落的方式插入文本 + const formattedText = `\n${text}\n`; // 执行插入操作 this.editor?.executeEdits('', [ { range: new this.monaco!.Range( - lastLineNumber, - lastColumn, - lastLineNumber, - lastColumn, + insertLine, + insertColumn, + insertLine, + insertColumn, // 光标位置纯插入,不替换任何内容 ), - text, + text: formattedText, }, ]); - // 计算插入后光标的新位置(在新增文本的末尾) - const newLastLine = lastLineNumber + (text.includes('\n') ? 1 : 0); // 若插入换行,行号+1 - const newLastColumn = text.split('\n').pop()?.length || 0; + // 计算插入后光标的新位置(停留在插入文本末尾,换行符之前) + const linesInText = formattedText.split('\n'); + const linesAdded = linesInText.length - 1; // 前后换行 + 文本内换行 = 总新增行数 + const lastLineOfInsert = insertLine + linesAdded - 1; // 排除末尾的换行符所在行 + const lastLineContent = linesInText[linesInText.length - 2] || ''; // 取文本最后一行(排除末尾换行) + const newColumn = lastLineContent.length; - this.editor?.setPosition( - new this.monaco!.Position(newLastLine, newLastColumn), - ); - this.editor?.revealPositionInCenter( - new this.monaco!.Position(newLastLine, newLastColumn), - ); + // 更新光标位置并聚焦 + const newPosition = new this.monaco!.Position(lastLineOfInsert, newColumn); + this.editor?.setPosition(newPosition); + this.editor?.revealPositionInCenter(newPosition); this.editor?.focus(); } @@ -225,10 +235,38 @@ export class CodeEditorController this.editor?.focus(); } + /** + * 获取内联AI编辑器元素 + */ + getInLineAiEditorElement(): Element { + if (!this.editor) { + throw new RuntimeError(ibiz.i18n.t('editor.code.editorNotInit')); + } + return this.editor.getDomNode() as Element; + } + + /** + * 获取内联AI编辑器主题 + */ + getInLineAiEditorTheme(): 'light' | 'dark' { + const currentTheme = (this.editor as IParams)?._themeService?._theme + ?.themeName; + switch (currentTheme) { + case 'vs-dark': + return 'dark'; + case 'vs': + default: + return 'light'; + } + } + /** * 获取内联AI聊天参数 */ getInLineAiChatOptions(): IInLineAiChatOptions { + if (!this.editor || !this.monaco) { + throw new RuntimeError(ibiz.i18n.t('editor.code.editorNotInit')); + } const contentArea = this.editor ?.getDomNode() ?.querySelector('.editor-scrollable') as HTMLElement; @@ -251,11 +289,21 @@ export class CodeEditorController } const rect = contentArea.getBoundingClientRect(); + // 编辑器布局信息 + const layoutInfo = this.editor.getLayoutInfo(); return { + // 编辑器编辑区左侧距离 left: rect.left, - top: editorRect.top + coordinates.top + 28, // 减去 20px 的行高度及 8px 向下偏移 - width: rect.width - 160, // 减去 左侧占位 及 右侧代码预览 边距 + // 编辑器上方距离 + 选区距离编辑器上方距离 + 行高度 + top: editorRect.top + coordinates.top + coordinates.height, + // 编辑器编辑区宽度 - 代码预览区宽度 - 代码预览区标尺宽度 + width: + rect.width - + layoutInfo.minimap.minimapWidth - + layoutInfo.overviewRuler.width, + editorElement: this.getInLineAiEditorElement(), + editorTheme: this.getInLineAiEditorTheme(), }; } diff --git a/src/editor/code/monaco-editor/monaco-editor.scss b/src/editor/code/monaco-editor/monaco-editor.scss index 951a8da2cd8576800c44825a0237b43b56c1cdc9..8a2bd62898e18524354d8bcc59cdc3d03a79af3c 100644 --- a/src/editor/code/monaco-editor/monaco-editor.scss +++ b/src/editor/code/monaco-editor/monaco-editor.scss @@ -1,9 +1,22 @@ +/* stylelint-disable color-function-notation */ +/* stylelint-disable alpha-value-notation */ /* stylelint-disable selector-class-pattern */ $code: ( + // Color + 'color-text-editor-toolbar-bg': getCssVar(color, bg, 2), + 'color-text-editor-toolbar-text': getCssVar(color, text, 0), + 'color-text-editor-toolbar-item-bg-hover': getCssVar(color, primary, light, default), + 'color-text-editor-toolbar-item-text-hover': getCssVar(color, primary), // Height/Width 'height-text-editor-toolbar': 40px, 'footer-toolbar-height': 36px, 'footer-button-height': 36px, + // Spacing + 'spacing-code-text-editor-toolbar-padding': getCssVar('spacing', 'extra-tight'), + 'spacing-code-text-editor-toolbar-item-padding': getCssVar('spacing', 'extra-tight') getCssVar('spacing', 'tight'), + // Radius + 'spacing-code-text-editor-toolbar-circle': getCssVar('border-radius', 'extra-small'), + 'spacing-code-text-editor-toolbar-item-circle': getCssVar('border-radius', 'extra-small'), // Other 'text-editor-toolbar-z-index': 1, 'text-editor-toolbar-left': 0, @@ -11,6 +24,8 @@ $code: ( ); @include b(code) { + @include set-component-css-var('code', $code); + display: flex; flex-direction: column; width: 100%; @@ -62,8 +77,6 @@ $code: ( } @include b('code-footer') { - @include set-component-css-var('code', $code); - display: flex; align-items: center; justify-content: end; @@ -136,9 +149,6 @@ $code: ( } @include b('code-editor-enable') { - - @include set-component-css-var('code', $code); - .#{bem(code__box)} { height: 100%; } @@ -191,8 +201,6 @@ $code: ( } @include b('code-text-editor-toolbar') { - @include set-component-css-var('code', $code); - position: fixed; top: getCssVar('code', 'text-editor-toolbar-top'); left: getCssVar('code', 'text-editor-toolbar-left'); @@ -200,23 +208,33 @@ $code: ( display: flex; gap: getCssVar('spacing', 'tight'); height: getCssVar('code', 'height-text-editor-toolbar'); - padding: getCssVar('spacing', 'extra-tight'); + padding: getCssVar('code', 'spacing-code-text-editor-toolbar-padding'); font-size: getCssVar('font-size', 'regular'); - background-color: getCssVar(color, bg, 2); - border-radius: getCssVar('border-radius', 'extra-small'); + color: getCssVar('code', 'color-text-editor-toolbar-text'); + background-color: getCssVar('code', 'color-text-editor-toolbar-bg'); + border-radius: getCssVar('code', 'spacing-code-text-editor-toolbar-circle'); box-shadow: getCssVar(shadow, elevated); @include e('item') { display: flex; align-items: center; justify-content: center; - padding: getCssVar('spacing', 'extra-tight') getCssVar('spacing', 'tight'); + padding: getCssVar('code', 'spacing-code-text-editor-toolbar-item-padding'); cursor: pointer; - border-radius: getCssVar('border-radius', 'extra-small'); + border-radius: getCssVar('code', 'spacing-code-text-editor-toolbar-item-circle'); &:hover { - color: getCssVar(color, primary); - background-color: getCssVar(color, primary, light, default); + color: getCssVar('code', 'color-text-editor-toolbar-item-text-hover'); + background-color: getCssVar('code', 'color-text-editor-toolbar-item-bg-hover'); } } +} + +@include b(code) { + @include when('dark') { + #{getCssVarName('code-color-text-editor-toolbar-bg')}: rgba(53 54 60 / 100%); + #{getCssVarName('code-color-text-editor-toolbar-text')}: rgba(249, 249, 249, 1); + #{getCssVarName('code-color-text-editor-toolbar-item-bg-hover')}: rgba(255, 255, 255, 0.12); + #{getCssVarName('code-color-text-editor-toolbar-item-text-hover')}: rgba(249, 249, 249, 1); + } } \ No newline at end of file diff --git a/src/editor/code/monaco-editor/monaco-editor.tsx b/src/editor/code/monaco-editor/monaco-editor.tsx index 8344180719cf33d6c6fa9f5e65bb38efc6741928..95da101014b3a9274f45548a40474d287b8e8b73 100644 --- a/src/editor/code/monaco-editor/monaco-editor.tsx +++ b/src/editor/code/monaco-editor/monaco-editor.tsx @@ -97,6 +97,9 @@ export const IBizCode = defineComponent({ // 文本编辑工具栏可见状态 const textTBVisible = ref(false); + // 当前编辑器主题 + const editorTheme = ref(''); + const editorModel = c.model; if (editorModel.editorParams) { if (editorModel.editorParams.enableEdit) { @@ -144,6 +147,8 @@ export const IBizCode = defineComponent({ // 编辑器主题 const getMonacoTheme = (name: string): string => { + editorTheme.value = + c?.editorParams?.customTheme || ibiz.config.codeEditorTheme || name; // customTheme参数已弃用,后续请使用全局参数codeEditorTheme const customTheme = c?.editorParams?.customTheme; if (customTheme) { @@ -506,11 +511,13 @@ export const IBizCode = defineComponent({ textTBStyle.value = { ...textTBStyle.value, + // 编辑器左侧距离 + 选区距离编辑器左侧距离 [ns.cssVarBlockName('text-editor-toolbar-left')]: `${ editorRect.left + coordinates.left }px`, + // 编辑器上方距离 + 选区距离编辑器上方距离 + 行高度 [ns.cssVarBlockName('text-editor-toolbar-top')]: `${ - editorRect.top + coordinates.top + 28 + editorRect.top + coordinates.top + coordinates.height }px`, }; } @@ -540,7 +547,8 @@ export const IBizCode = defineComponent({ if (!position) return; const coordinates = editor?.getScrolledVisiblePosition(position); const editorRect = editor?.getDomNode()?.getBoundingClientRect(); - if (!coordinates || !editorRect) return; + const textTBHeight = textTBRef.value.offsetHeight; + if (!coordinates || !editorRect || !textTBHeight) return; const items: MenuItem[] = ibiz.inLineAIUtil.calcContextMenus( c.deACMode, (tag: string) => { @@ -549,8 +557,10 @@ export const IBizCode = defineComponent({ ); if (items.length === 0) return; ibiz.inLineAIUtil.showContextMenus( + // 编辑器左侧距离 + 选区距离编辑器左侧距离 editorRect.left + coordinates.left, - editorRect.top + coordinates.top + 28 + 40, + // 编辑器上方距离 + 选区距离编辑器上方距离 + 行高度 + 工具栏高度 + editorRect.top + coordinates.top + coordinates.height + textTBHeight, items, ); }; @@ -898,6 +908,7 @@ export const IBizCode = defineComponent({ readonlyState, isLoading, textTBRef, + editorTheme, renderFooter, renderHeaderToolbar, renderTextEditorToolbar, @@ -911,6 +922,7 @@ export const IBizCode = defineComponent({