Skip to content

Commit 7a46787

Browse files
authored
feat: support extension register panel to ai chat (#4012)
1 parent 6e2ecab commit 7a46787

9 files changed

Lines changed: 130 additions & 12 deletions

File tree

packages/ai-native/src/browser/ai-core.contribution.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,12 @@ import { IntelligentCompletionsController } from './contrib/intelligent-completi
8484
import { ProblemFixController } from './contrib/problem-fix/problem-fix.controller';
8585
import { RenameSingleHandler } from './contrib/rename/rename.handler';
8686
import { AIRunToolbar } from './contrib/run-toolbar/run-toolbar';
87-
import { AIChatTabRenderer, AILeftTabRenderer, AIRightTabRenderer } from './layout/tabbar.view';
87+
import {
88+
AIChatTabRenderer,
89+
AIChatTabRendererWithTab,
90+
AILeftTabRenderer,
91+
AIRightTabRenderer,
92+
} from './layout/tabbar.view';
8893
import { AIChatLogoAvatar } from './layout/view/avatar/avatar.view';
8994
import {
9095
AINativeCoreContribution,
@@ -446,7 +451,12 @@ export class AINativeBrowserContribution
446451
}
447452

448453
registerRenderer(registry: SlotRendererRegistry): void {
449-
registry.registerSlotRenderer(AI_CHAT_VIEW_ID, AIChatTabRenderer);
454+
if (this.designLayoutConfig.supportExternalChatPanel) {
455+
registry.registerSlotRenderer(AI_CHAT_VIEW_ID, AIChatTabRendererWithTab);
456+
} else {
457+
registry.registerSlotRenderer(AI_CHAT_VIEW_ID, AIChatTabRenderer);
458+
}
459+
450460
if (this.designLayoutConfig.useMergeRightWithLeftPanel) {
451461
registry.registerSlotRenderer(SlotLocation.left, AILeftTabRenderer);
452462
registry.registerSlotRenderer(SlotLocation.right, AIRightTabRenderer);
@@ -456,6 +466,8 @@ export class AINativeBrowserContribution
456466
registerComponent(registry: ComponentRegistry): void {
457467
registry.register(AI_CHAT_CONTAINER_ID, [], {
458468
component: AIChatView,
469+
title: localize('aiNative.chat.ai.assistant.name'),
470+
iconClass: getIcon('magic-wand'),
459471
containerId: AI_CHAT_CONTAINER_ID,
460472
});
461473
registry.register(AI_MENU_BAR_DEBUG_TOOLBAR, {

packages/ai-native/src/browser/layout/layout.module.less

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,7 @@
169169
.ai_chat_view_container {
170170
background-color: unset;
171171
}
172+
173+
.AI-Chat-slot {
174+
background-color: var(--activityBar-background) !important;
175+
}

packages/ai-native/src/browser/layout/tabbar.view.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { localize } from '@opensumi/ide-core-common';
1515
import { DesignLeftTabRenderer, DesignRightTabRenderer } from '@opensumi/ide-design/lib/browser/layout/tabbar.view';
1616
import { IMainLayoutService } from '@opensumi/ide-main-layout';
1717
import {
18+
ChatTabbarRenderer2,
1819
IconElipses,
1920
IconTabView,
2021
LeftTabbarRenderer,
@@ -67,6 +68,31 @@ export const AIChatTabRenderer = ({
6768
/>
6869
);
6970

71+
export const AIChatTabRendererWithTab = ({
72+
className,
73+
components,
74+
}: {
75+
className: string;
76+
components: ComponentRegistryInfo[];
77+
}) => (
78+
<TabRendererBase
79+
side={AI_CHAT_VIEW_ID}
80+
direction={EDirection.RightToLeft}
81+
id={styles.ai_chat_panel}
82+
className={cls(className, `${AI_CHAT_VIEW_ID}-slot`, 'design_right_slot')}
83+
components={components}
84+
TabbarView={() => <ChatTabbarRenderer2 />}
85+
TabpanelView={() => (
86+
<BaseTabPanelView
87+
PanelView={ContainerView}
88+
PanelViewProps={{
89+
className: styles.ai_chat_view_container,
90+
}}
91+
/>
92+
)}
93+
/>
94+
);
95+
7096
export const AILeftTabRenderer = ({
7197
className,
7298
components,

packages/core-browser/src/layout/constants.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ export class DesignLayoutConfig implements IDesignLayoutConfig {
157157
useMergeRightWithLeftPanel: false,
158158
useMenubarView: false,
159159
menubarLogo: '',
160+
supportExternalChatPanel: false,
160161
};
161162

162163
setLayout(...value: (Partial<IDesignLayoutConfig> | undefined)[]): void {
@@ -170,4 +171,8 @@ export class DesignLayoutConfig implements IDesignLayoutConfig {
170171
get menubarLogo(): string {
171172
return this.internalLayout.menubarLogo;
172173
}
174+
175+
get supportExternalChatPanel(): boolean {
176+
return this.internalLayout.supportExternalChatPanel;
177+
}
173178
}

packages/core-common/src/types/ai-native/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export interface IDesignLayoutConfig {
6060
* set menubar logo
6161
*/
6262
menubarLogo?: string;
63+
supportExternalChatPanel?: boolean;
6364
}
6465

6566
export interface IAINativeConfig {

packages/extension/src/browser/vscode/contributes/view-containers.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { Autowired, Injectable } from '@opensumi/di';
2+
import { AI_CHAT_VIEW_ID } from '@opensumi/ide-ai-native';
23
import { DisposableCollection, LifeCyclePhase } from '@opensumi/ide-core-common';
34
import { IMainLayoutService } from '@opensumi/ide-main-layout';
45
import { IIconService, IconType } from '@opensumi/ide-theme';
56

67
import { Contributes, LifeCycle, VSCodeContributePoint } from '../../../common';
78
import { AbstractExtInstanceManagementService } from '../../types';
89

9-
type LocationKey = 'panel' | 'activitybar';
10+
type LocationKey = 'panel' | 'activitybar' | 'ai-chat';
1011

1112
export type ViewContainersContribution = {
1213
[key in LocationKey]: ViewContainerItem;
@@ -35,11 +36,17 @@ export class ViewContainersContributionPoint extends VSCodeContributePoint<ViewC
3536

3637
private disposableCollection: DisposableCollection = new DisposableCollection();
3738

38-
private convertLocationToSide(location: 'activitybar' | 'panel'): 'left' | 'bottom' {
39-
if (location === 'activitybar') {
40-
return 'left';
39+
private convertLocationToSide(location: LocationKey): 'left' | 'bottom' | typeof AI_CHAT_VIEW_ID {
40+
switch (location) {
41+
case 'activitybar': {
42+
return 'left';
43+
}
44+
case 'ai-chat': {
45+
return AI_CHAT_VIEW_ID;
46+
}
47+
default:
48+
return 'bottom';
4149
}
42-
return 'bottom';
4350
}
4451

4552
contribute() {
@@ -50,7 +57,8 @@ export class ViewContainersContributionPoint extends VSCodeContributePoint<ViewC
5057
continue;
5158
}
5259
for (const location of Object.keys(contributes)) {
53-
const side = this.convertLocationToSide(location as LocationKey);
60+
const side: string = this.convertLocationToSide(location as LocationKey);
61+
5462
for (const container of contributes[location]) {
5563
const handlerId = this.mainlayoutService.collectTabbarComponent(
5664
[],

packages/main-layout/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
},
2020
"dependencies": {
2121
"@opensumi/ide-components": "workspace:*",
22-
"@opensumi/ide-core-common": "workspace:*"
23-
},
24-
"devDependencies": {
22+
"@opensumi/ide-core-common": "workspace:*",
2523
"@opensumi/ide-core-browser": "workspace:*",
26-
"@opensumi/ide-dev-tool": "workspace:*",
2724
"@opensumi/ide-theme": "workspace:*"
25+
},
26+
"devDependencies": {
27+
"@opensumi/ide-dev-tool": "workspace:*"
2828
}
2929
}

packages/main-layout/src/browser/tabbar/bar.view.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,3 +387,27 @@ export const BottomTabbarRenderer: React.FC = () => {
387387
</div>
388388
);
389389
};
390+
391+
export const ChatTabbarRenderer2: React.FC<{ barSize?: number; style?: React.CSSProperties }> = (props) => {
392+
const { barSize = 32, style } = props;
393+
const { side } = React.useContext(TabbarConfig);
394+
const tabbarService: TabbarService = useInjectable(TabbarServiceFactory)(side);
395+
useEffect(() => {
396+
tabbarService.setIsLatter(true);
397+
}, [tabbarService]);
398+
const styles_right_tab_bar = useDesignStyles(styles.ai_right_tab_bar, 'ai_right_tab_bar');
399+
const styles_right_tab = useDesignStyles(styles.ai_right_tab, 'ai_right_tab');
400+
401+
return (
402+
<div id={side} className={styles_right_tab_bar} style={style} onContextMenu={tabbarService.handleContextMenu}>
403+
<TabbarViewBase
404+
tabSize={32}
405+
MoreTabView={IconElipses}
406+
tabClassName={styles_right_tab}
407+
TabView={IconTabView}
408+
barSize={barSize}
409+
panelBorderSize={1}
410+
/>
411+
</div>
412+
);
413+
};

packages/main-layout/src/browser/tabbar/styles.module.less

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,3 +430,41 @@
430430
}
431431
}
432432
}
433+
434+
.ai_right_tab_bar {
435+
height: 100%;
436+
display: flex;
437+
flex-direction: column;
438+
justify-content: space-between;
439+
background-color: var(--activityBar-background);
440+
border-left: 1px solid var(--activityBar-border);
441+
border-top: 1px solid var(--activityBar-border);
442+
443+
div[class*='tab_bar'] {
444+
height: 100%;
445+
div[class*='icon_tab'] {
446+
margin: 0;
447+
margin-bottom: 4px;
448+
}
449+
}
450+
451+
div[class*='design-bar_content'] {
452+
width: 100%;
453+
overflow-y: auto;
454+
455+
:global(.active) {
456+
div[class*='design-icon_tab'] {
457+
background-color: var(--badge-background);
458+
color: var(--badge-foreground);
459+
460+
&::before {
461+
content: none;
462+
}
463+
}
464+
}
465+
466+
&::-webkit-scrollbar {
467+
display: none !important;
468+
}
469+
}
470+
}

0 commit comments

Comments
 (0)