Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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