Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/ai-native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"@xterm/xterm": "5.5.0",
"ai": "^4.1.45",
"ansi-regex": "^2.0.0",
"ansi_up": "^5.1.0",
"diff": "^7.0.0",
"dom-align": "^1.7.0",
"eventsource": "^3.0.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/ai-native/src/browser/components/WelcomeMsg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export const WelcomeMessage = () => {
return (
<div className={styles.chat_welcome_head}>
<div className={styles.chat_container_des}>
{isMarkdownString(welcomeMessage) ? <ChatMarkdown markdown={welcomeMessage} /> : welcomeMessage}
{isMarkdownString(welcomeMessage) ? <ChatMarkdown key='welcome' markdown={welcomeMessage} /> : welcomeMessage}
</div>
<div className={styles.chat_container_content}>
{allSampleQuestions.map((data: any, index) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useEffect, useMemo, useState } from 'react';

import { Icon } from '@opensumi/ide-components/lib/icon/icon';
import { CommandService, LabelService, URI, path, useInjectable } from '@opensumi/ide-core-browser';
import { WorkbenchEditorService } from '@opensumi/ide-editor';
import { IWorkspaceService } from '@opensumi/ide-workspace';
Expand Down Expand Up @@ -110,7 +111,9 @@ const ExpandableFileList: React.FC<ExpandableFileListProps> = ({
return (
<div className={styles.container}>
<div className={styles.header} onClick={() => setIsExpanded(!isExpanded)}>
<span style={{ transform: `rotate(${isExpanded ? '90deg' : '0deg'})` }}>โ–ถ</span>
<span style={{ transform: `rotate(${isExpanded ? '90deg' : '0deg'})`, display: 'flex' }}>
<Icon iconClass={'codicon codicon-chevron-right'} />
</span>
<span>
{headerText} ยท {fileList.length} files
</span>
Expand Down
10 changes: 4 additions & 6 deletions packages/ai-native/src/browser/mcp/tools/components/Terminal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import React, { memo, useCallback, useMemo, useState } from 'react';
import { useInjectable } from '@opensumi/ide-core-browser';
import { Button, Icon } from '@opensumi/ide-core-browser/lib/components';
import { localize } from '@opensumi/ide-core-common';
import { stripAnsi } from '@opensumi/ide-utils/lib/ansi';

import { IMCPServerToolComponentProps } from '../../../types';
import { RunCommandHandler } from '../handlers/RunCommand';

import { computeAnsiLogString } from './computeAnsiLogString';
import styles from './index.module.less';

function getResult(raw: string) {
Expand Down Expand Up @@ -63,19 +63,17 @@ export const TerminalToolComponent = memo((props: IMCPServerToolComponentProps)
<>
<div className={styles.command_title}>
<Icon icon='terminal' />
<span>{localize('ai.native.mcp.terminal.command')}</span>
<span>{localize('ai.native.mcp.terminal.command')}:</span>
</div>
<p className={styles.command_content}>
<code>$ {args.command}</code>
</p>
</>
)}
<div className={styles.command_title}>
<span>{localize('ai.native.mcp.terminal.output')}</span>
</div>
{output ? (
<div className={styles.command_content}>
<code>{stripAnsi(output.text)}</code>
<Icon icon='output' />
<code dangerouslySetInnerHTML={{ __html: computeAnsiLogString(output.text || '') }} />
Comment thread
Aaaaash marked this conversation as resolved.
</div>
) : (
''
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import AnsiUp from 'ansi_up';

import filterEraseMultipleLine from './filterEraseMultipleLine';

type LogContent = string;

const ansiUp = new AnsiUp();

export function computeAnsiLogString(logs: LogContent, enableEraseLineFilter = true, hideEmptyLine = false): string {
const splittedLogs = logs.split('\n');
// ๅค„็†ๆธ…็ฉบไธŠ่กŒ้€ป่พ‘
// ไธŠ็งป cursor + ๆธ…็ฉบๆ•ด่กŒ
let filteredLogs = enableEraseLineFilter ? filterEraseMultipleLine(splittedLogs) : splittedLogs;
if (hideEmptyLine) {
filteredLogs = filteredLogs.map((line) => line.replace('\r', '')).filter((line) => !!line);
}

const htmlLogLines = filteredLogs.map((line) => {
const htmlLog = ansiUp.ansi_to_html(line);

return htmlLog;
});
return htmlLogLines.join('\n');
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
export const ESC = '\u001B[';
export const eraseLine = ESC + '2K';
export const eraseEndLine = ESC + 'K';

export const cursorUp = (count = 1) => ESC + count + 'A';

/**
* ๅค„็†่ฟ‡ๆปคๆธ…็ฉบไธŠ่กŒ๏ผŒๆธ…็ฉบๆœฌ่กŒ้€ป่พ‘ใ€‚
*
* ๅ…ณไบŽๆธ…็ฉบไธŠ n ่กŒ๏ผš
* ไธ€่ˆฌๅœจๆ—ฅๅฟ—ไธญ๏ผŒๅ‡บ็Žฐ่ฆ†็›–ไธŠ่กŒ็š„ๆƒ…ๅ†ต๏ผŒascii ็ผ–็ ไธบ 2K [1A 2K ...] 1G ็š„ๆ ทๅผใ€‚ ๅฆ‚ \u001b[2K\u001b[1A\u001b[2K\u001b[1A\u001b[2K\u001b[G\r\n๏ผŒไปฃ่กจๆธ…็ฉบไธŠไธค่กŒใ€‚
* ๅ…ถไธญ๏ผŒ2K ไปฃ่กจๆธ…็ฉบๆ•ด่กŒ๏ผŒ1A ไปฃ่กจๅ…‰ๆ ‡ไธŠ็งป๏ผŒ้…ๅˆไธ‹ไธ€ไธช 2K ๅˆ™ๆœ€็ปˆๆ•ˆๆžœไธบๆธ…็ฉบไธŠ่กŒ๏ผŒ่€Œ 1G ๆ˜ฏ็งปๅŠจๅ…‰ๆ ‡ๅˆฐๆœฌ่กŒๅผ€ๅง‹๏ผˆไฝ็ฝฎ 1๏ผ‰ใ€‚
* ๅœจๆ—ฅๅฟ—่ฟ‡ๆปค่ฟ‡็จ‹ไธญ๏ผŒๅฏไปฅๅชๅค„็† 1A 2K ่ฟ™ไธชๅบๅˆ—๏ผŒ้‡ๅˆฐๅŽๆŠŠ่ฏฅๆ—ฅๅฟ—็š„ไธŠไธ€่กŒๅˆ ๆމๅณๅฏใ€‚
*
* ๅ…ณไบŽๆธ…็ฉบๆœฌ่กŒ๏ผŒๆŒ‰้กบๅบๆ‰ง่กŒ๏ผš
* 1. ๅœจๅฝ“ๅ‰่กŒๆฒกๆœ‰ Cursor ๆ“ไฝœ็ฌฆ๏ผˆๅฆ‚ไธŠ็งปๆ—ถ๏ผ‰๏ผŒๅŒน้…ๆœ€ๅŽไธ€ไธช [2K ๏ผˆๆธ…็ฉบๆœฌ่กŒ๏ผ‰ๆˆ– \r[K๏ผˆๆŒ‡้’ˆๅ›ž 0๏ผŒๅ†ๆธ…็ฉบๆœฌ่กŒๅˆฐๆœซๅฐพ๏ผŒ็›ธๅฝ“ไบŽๆธ…็ฉบๆœฌ่กŒ๏ผ‰๏ผŒๅช่พ“ๅ‡บ [2K ๅŽ็š„ๅ†…ๅฎน๏ผŒ
* 2. ๅœจๅฝ“ๅ‰่กŒๆฒกๆœ‰ Cursor ๆ“ไฝœ็ฌฆๆ—ถไธ”ๆœ‰ๅคšไธช \r ๏ผˆcarriage return charactor๏ผŒ็งปๅŠจๅ…‰ๆ ‡ๅˆฐ่กŒ้ฆ–๏ผ‰ๆ—ถ๏ผŒreduce ๆŒ‰ \r \x1b[G ๆˆ– \x1b[1G ๅˆ‡ๅˆ†็š„็‰‡ๆ–ญ๏ผŒไธๆฎต็”จๅŽไธ€ part ็š„้ƒจๅˆ†ไปŽๅคด่ฆ†็›–ๅพ—ๅ‡บ็ป“ๆžœใ€‚
*/
export default function filterEraseMultipleLine(logs: string[]) {
// ไธŠ็งป cursor + ๆธ…็ฉบๆ•ด่กŒ
const eraseLastLine = cursorUp(1) + eraseLine;
const eraseCurrentLine = eraseLine;
const eraseCurrentLine2 = `\r${eraseEndLine}`;

const moveCursorToLeftRegStrs = ['\\r', '\\u001b\\[G', '\\u001b\\[1G'];
const moveCursorToLeftRegStr = new RegExp(`${moveCursorToLeftRegStrs.join('|')}`);

const filteredLogs = logs.reduce((acc: string[], nowLine) => {
// ๅฝ“ๅ‰ๆธ…็ฉบไธŠ่กŒๆœ็ดขๆŒ‡้’ˆ
let pos = 0;
const step = eraseLastLine.length;

while (true) {
pos = nowLine.indexOf(eraseLastLine, pos);
// ๅ‡บ็Žฐๆธ…็ฉบไธŠ่กŒ
if (pos >= 0) {
pos += step;
acc.pop();
} else {
break;
}
}

// ๅฏนๅ•่กŒๆ—ฅๅฟ—็š„้‡ๅ†™ๅšๅค„็†
// ็ฎ€ๅ•ๅค„็†๏ผŒไธๅŽป่งฃๆž็œŸๆญฃ็š„ Cursor ๆ‰€ๅœจ่กŒ๏ผŒๅฆๅˆ™้€ป่พ‘่ฟ‡ไบŽ้บป็ƒฆ
// ๅค„็† [2K
let lastErasePos = nowLine.lastIndexOf(eraseCurrentLine);
if (lastErasePos < 0) {
// ๅค„็† \r[K
lastErasePos = nowLine.lastIndexOf(eraseCurrentLine2);
}
if (lastErasePos > 0) {
// ไปŽๅŽๅ‘ๅ‰ๆœ็ดขๆœ€ๅŽไธ€ไธชๆธ…่กŒๆ“ไฝœ
nowLine = nowLine.slice(lastErasePos);
}

// ๅค„็†ๅคš \r ๆƒ…ๅ†ต๏ผŒๅฝ“ \r ่ฟž็ปญๆ—ถ๏ผŒๅˆ‡ๅˆ†ๅ‡บ็š„็ฉบๅญ—ๆฎตๆ— ็”จ๏ผŒ่ฟ‡ๆปคๆމ
const carriageRewrites = nowLine.split(moveCursorToLeftRegStr).filter((part) => !!part);
if (carriageRewrites.length > 1) {
nowLine = carriageRewrites.reduce((nextNowLine, nowPart) => {
const leftPart = nextNowLine.slice(nowPart.length);
return nowPart + leftPart;
}, '');
}

acc.push(nowLine);
return acc;
}, []);

return filteredLogs;
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,15 @@
}

.header {
padding: 8px 12px;
padding: 4px;
background-color: var(--design-block-background);
border-bottom: 1px solid var(--vscode-commandCenter-inactiveBorder);
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
color: var(--design-text-foreground);
font-size: 12px;
font-size: 11px;
}

.fileList {
Expand Down Expand Up @@ -162,14 +162,17 @@
.command_title {
display: flex;
align-items: center;
font-size: 11px;
span {
margin-left: 5px;
}
}

.command_content {
padding: 4px;
font-size: 12px;
max-height: 200px;
overflow-y: auto;
padding: 2px 4px;
font-size: 11px;
color: var(--design-text-foreground);
margin: 0px;
background-color: var(--terminal-background);
Expand All @@ -178,7 +181,7 @@
overflow: auto;

code {
font-size: 12px;
font-size: 11px;
white-space: pre;
}
}
Expand Down
31 changes: 21 additions & 10 deletions packages/components/src/markdown-react/parse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,12 @@ export class MarkdownReactParser extends marked.Renderer {

listItemChildren.push(this.parse(item.tokens));

return React.cloneElement(this.renderer.listItem(listItemChildren) as React.ReactElement, {
key: `list-item-${itemIndex}`,
});
return React.cloneElement(
this.renderer.listItem(listItemChildren) as React.ReactElement,
{
key: `list-item-${itemIndex}`,
},
);
});

return this.renderer.list(children, token.ordered);
Expand All @@ -83,9 +86,12 @@ export class MarkdownReactParser extends marked.Renderer {
),
);

const headerRow = React.cloneElement(this.renderer.tableRow(headerCells) as React.ReactElement, {
key: 'header-row',
});
const headerRow = React.cloneElement(
this.renderer.tableRow(headerCells) as React.ReactElement,
{
key: 'header-row',
},
);
const header = this.renderer.tableHeader(headerRow);

const bodyChilren = tableToken.rows.map((row, rowIndex) => {
Expand All @@ -99,9 +105,12 @@ export class MarkdownReactParser extends marked.Renderer {
),
);

return React.cloneElement(this.renderer.tableRow(rowChildren) as React.ReactElement, {
key: `body-row-${rowIndex}`,
});
return React.cloneElement(
this.renderer.tableRow(rowChildren) as React.ReactElement,
{
key: `body-row-${rowIndex}`,
},
);
});

const body = this.renderer.tableBody(bodyChilren);
Expand Down Expand Up @@ -185,5 +194,7 @@ export class MarkdownReactParser extends marked.Renderer {
function htmlUnescape(htmlStr) {
return htmlStr
.replace(/&#(\d+);/g, (match, dec) => String.fromCharCode(dec))
.replace(/&#x([0-9A-Fa-f]+);/g, (match, hex) => String.fromCharCode(parseInt(hex, 16)));
.replace(/&#x([0-9A-Fa-f]+);/g, (match, hex) =>
String.fromCharCode(parseInt(hex, 16)),
);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable no-console */
import { BinaryWriter } from '@furyjs/fury/dist/lib/writer';

import { MaybeNull, readUInt32LE } from '@opensumi/ide-core-common';
import { MaybeNull, readUInt32LE, setImmediate } from '@opensumi/ide-core-common';

import { Buffers } from '../../buffers/buffers';

Expand Down
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3419,6 +3419,7 @@ __metadata:
"@xterm/xterm": "npm:5.5.0"
ai: "npm:^4.1.45"
ansi-regex: "npm:^2.0.0"
ansi_up: "npm:^5.1.0"
diff: "npm:^7.0.0"
dom-align: "npm:^1.7.0"
eventsource: "npm:^3.0.5"
Expand Down Expand Up @@ -7076,6 +7077,13 @@ __metadata:
languageName: node
linkType: hard

"ansi_up@npm:^5.1.0":
version: 5.2.1
resolution: "ansi_up@npm:5.2.1"
checksum: 10/abf4f2e4abc1f54ef41d5668b22967c33117faf31308211858ff3bb898f4839f97a7bde081d6eb988532a90a1ccd37f1ef5c0f0f3e409d1b96ed4c5bd41d11c7
languageName: node
linkType: hard

"antd@npm:^5.21.4":
version: 5.21.4
resolution: "antd@npm:5.21.4"
Expand Down