Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -1160,13 +1160,19 @@ export class AICustomizationManagementEditor extends EditorPane {
this.migrationBannerContainer = DOM.append(this.migrationContentContainer, $('.customization-migration-banner'));
this.migrationBannerContainer.style.display = 'none';

this.migrationListContainer = $('.prompt-migration-list.list-container.distributed-section-layout');
this.migrationListScrollable = this.editorDisposables.add(new DomScrollableElement(this.migrationListContainer, {
// Keep the native scroll target separate because virtualization can make the content overflow visible.
const migrationScrollContainer = $('.prompt-migration-scroll-container');
this.migrationListContainer = DOM.append(migrationScrollContainer, $('.prompt-migration-list.list-container.distributed-section-layout'));
const migrationListScrollable = this.editorDisposables.add(new DomScrollableElement(migrationScrollContainer, {
horizontal: ScrollbarVisibility.Hidden,
vertical: ScrollbarVisibility.Auto,
useShadows: false,
}));
const migrationListScrollableNode = this.migrationListScrollable.getDomNode();
this.migrationListScrollable = migrationListScrollable;
this.editorDisposables.add(DOM.addDisposableListener(migrationScrollContainer, DOM.EventType.SCROLL, () => {
migrationListScrollable.setScrollPosition({ scrollTop: migrationScrollContainer.scrollTop });
}));
const migrationListScrollableNode = migrationListScrollable.getDomNode();
migrationListScrollableNode.classList.add('prompt-migration-list-scrollable');
this.migrationContentContainer.appendChild(migrationListScrollableNode);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,12 +266,6 @@
box-sizing: border-box;
}

.tools-list-widget > .tools-gallery-container {
width: min(calc(100% - var(--vscode-spacing-size40)), 840px);
margin-inline: auto;
box-sizing: border-box;
}

.ai-customization-management-editor .contributed-section-container {
flex: 1 1 auto;
min-height: 0;
Expand Down Expand Up @@ -943,30 +937,57 @@ per-word capitalization does not survive translation. */
height: 100%;
display: flex;
flex-direction: column;
padding: 40px 40px 16px;
padding-block: var(--vscode-spacing-size400) var(--vscode-spacing-size160);
padding-inline: var(--vscode-spacing-size400);
background: var(--vscode-agentsPanel-background);
border: 1px solid var(--vscode-agentsPanel-border);
border-radius: 6px;
overflow: hidden;
box-sizing: border-box;
}

.ai-customization-management-editor .prompts-content-container,
.ai-customization-management-editor .prompt-migration-content-container,
.ai-customization-management-editor .tools-content-container {
padding-inline: var(--vscode-spacing-sizeNone);
}

/* The scroll hosts reach the pane edge while their content retains the panel inset. */
.ai-customization-management-editor .prompts-content-container > .ai-customization-list-widget.plugin-list-widget > .section-title-header,
.ai-customization-management-editor .prompts-content-container > .ai-customization-list-widget.plugin-list-widget > .list-search-and-button-container,
.ai-customization-management-editor .prompts-content-container > .ai-customization-list-widget.plugin-list-widget .plugin-card-scroll,
.ai-customization-management-editor .prompt-migration-content-container > .section-title-header,
.ai-customization-management-editor .prompt-migration-content-container > .customization-migration-banner,
.ai-customization-management-editor .prompt-migration-content-container > .prompt-migration-footer,
.ai-customization-management-editor .prompt-migration-list,
.ai-customization-management-editor .tools-content-container > .tools-list-widget > .section-title-header,
.ai-customization-management-editor .tools-content-container > .tools-list-widget > .tools-list-search-and-button-container,
.ai-customization-management-editor .tools-list-tree {
width: min(calc(100% - var(--vscode-spacing-size400) - var(--vscode-spacing-size400)), 840px);
margin-inline: auto;
}

.ai-customization-management-editor .tools-content-container > .tools-list-widget > .tools-gallery-container {
width: min(calc(100% - var(--vscode-spacing-size400) - var(--vscode-spacing-size400) - var(--vscode-spacing-size40)), 840px);
margin-inline: auto;
box-sizing: border-box;
}

.ai-customization-management-editor .prompts-content-container > .ai-customization-list-widget.plugin-list-widget > .plugin-card-scrollable {
max-width: none;
}

.ai-customization-management-editor .prompt-migration-list-scrollable {
position: relative;
flex: 1;
min-height: 0;
width: 100%;
max-width: 840px;
margin-inline: auto;
}

.ai-customization-management-editor .prompt-migration-content-container > .section-title-header,
.ai-customization-management-editor .prompt-migration-content-container > .customization-migration-banner,
.ai-customization-management-editor .prompt-migration-content-container > .prompt-migration-list-scrollable,
.ai-customization-management-editor .prompt-migration-content-container > .prompt-migration-footer {
width: min(100%, 840px);
margin-inline: auto;
box-sizing: border-box;
.ai-customization-management-editor .prompt-migration-list-scrollable > .prompt-migration-scroll-container {
position: absolute;
inset: 0;
overflow: hidden;
}

.ai-customization-management-editor .prompt-migration-footer {
Expand Down Expand Up @@ -1125,8 +1146,8 @@ per-word capitalization does not survive translation. */
}

.ai-customization-management-editor .prompt-migration-list {
position: absolute;
inset: 0;
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
Expand Down Expand Up @@ -1435,15 +1456,18 @@ per-word capitalization does not survive translation. */
flex: 1;
min-height: 0;
width: 100%;
max-width: 840px;
margin: 0 auto;
}

/* The scrolled element: bound it to the wrapper so clientHeight < scrollHeight when content overflows. */
.ai-customization-management-editor .tools-list-tree {
.ai-customization-management-editor .tools-list-tree-scrollable > .tools-list-scroll-container {
position: absolute;
inset: 0;
overflow: hidden;
}

.ai-customization-management-editor .tools-list-tree {
height: 100%;
min-height: 0;
overflow: hidden;
display: flex;
flex-direction: column;
gap: var(--vscode-spacing-size100);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -460,15 +460,19 @@ export class ToolsListWidget extends Disposable {
this._createHeader();
this._createSearchRow();

// Wrap the tree in a DomScrollableElement for an overlay scrollbar (not the native one).
this._treeContainer = $('.tools-list-tree');
this._treeContainer.classList.add('distributed-section-layout');
this._treeScrollable = this._register(new DomScrollableElement(this._treeContainer, {
// Keep the native scroll target separate because virtualization can make the content overflow visible.
const treeScrollContainer = $('.tools-list-scroll-container');
this._treeContainer = DOM.append(treeScrollContainer, $('.tools-list-tree.distributed-section-layout'));
const treeScrollable = this._register(new DomScrollableElement(treeScrollContainer, {
horizontal: ScrollbarVisibility.Hidden,
vertical: ScrollbarVisibility.Auto,
useShadows: false,
}));
const treeScrollableNode = this._treeScrollable.getDomNode();
this._treeScrollable = treeScrollable;
this._register(DOM.addDisposableListener(treeScrollContainer, DOM.EventType.SCROLL, () => {
treeScrollable.setScrollPosition({ scrollTop: treeScrollContainer.scrollTop });
}));
const treeScrollableNode = treeScrollable.getDomNode();
treeScrollableNode.classList.add('tools-list-tree-scrollable');
this.element.appendChild(treeScrollableNode);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -666,8 +666,9 @@ function makeFixtureTool(id: string, displayName: string, description: string, s
};
}

const fixtureToolExtensionId = 'acme.agent-tools';
const fixtureToolExtension = new class extends mock<IExtension>() {
override readonly identifier = { id: 'acme.agent-tools', uuid: undefined };
override readonly identifier = { id: fixtureToolExtensionId, uuid: undefined };
override readonly displayName = 'Acme Agent Tools';
override readonly publisherDisplayName = 'Acme';
override readonly description = 'Issue tracking and deployment tools for agents.';
Expand Down Expand Up @@ -695,17 +696,34 @@ const fixtureToolSets: readonly IToolSet[] = [
description: 'Acme Agent Tools',
detail: 'Tools contributed by the Acme extension.',
icon: Codicon.extensions,
source: { type: 'extension', label: 'Acme Agent Tools', extensionId: new ExtensionIdentifier('acme.agent-tools') },
source: { type: 'extension', label: 'Acme Agent Tools', extensionId: new ExtensionIdentifier(fixtureToolExtensionId) },
getTools: () => [
makeFixtureTool('acme.issues', 'Find Issues', 'Find and summarize open issues.', { type: 'extension', label: 'Acme Agent Tools', extensionId: new ExtensionIdentifier('acme.agent-tools') }),
makeFixtureTool('acme.deploy', 'Create Deployment', 'Create a deployment for the current project.', { type: 'extension', label: 'Acme Agent Tools', extensionId: new ExtensionIdentifier('acme.agent-tools') }),
makeFixtureTool('acme.issues', 'Find Issues', 'Find and summarize open issues.', { type: 'extension', label: 'Acme Agent Tools', extensionId: new ExtensionIdentifier(fixtureToolExtensionId) }),
makeFixtureTool('acme.deploy', 'Create Deployment', 'Create a deployment for the current project.', { type: 'extension', label: 'Acme Agent Tools', extensionId: new ExtensionIdentifier(fixtureToolExtensionId) }),
],
},
];

const overflowingExtensionToolSets: readonly IToolSet[] = Array.from({ length: 8 }, (_, index) => {
const toolSetNumber = index + 1;
const source: ToolDataSource = { type: 'extension', label: 'Acme Agent Tools', extensionId: new ExtensionIdentifier(fixtureToolExtensionId) };
return {
id: `acme-agent-tools-${toolSetNumber}`,
referenceName: `acme${toolSetNumber}`,
description: `Acme Tool Set ${toolSetNumber}`,
detail: `Extension tool set ${toolSetNumber}`,
icon: Codicon.extensions,
source,
getTools: () => [
makeFixtureTool(`acme.tool-${toolSetNumber}`, `Acme Tool ${toolSetNumber}`, `Tool from extension set ${toolSetNumber}`, source),
],
};
});

interface IRenderEditorOptions {
readonly sessionResource: URI;
readonly files?: readonly IFixtureFile[];
readonly toolSets?: readonly IToolSet[];
readonly configuration?: Record<string, unknown>;
readonly isSessionsWindow?: boolean;
readonly managementSections?: readonly AICustomizationManagementSection[];
Expand Down Expand Up @@ -770,6 +788,7 @@ async function renderEditor(ctx: ComponentFixtureContext, options: IRenderEditor
];

const allMcpServers = [...mcpWorkspaceServers, ...mcpUserServers];
const toolSets = options.toolSets ?? fixtureToolSets;
const selectedPromptType = options.selectedSection === AICustomizationManagementSection.Agents ? PromptsType.agent
: options.selectedSection === AICustomizationManagementSection.Skills ? PromptsType.skill
: options.selectedSection === AICustomizationManagementSection.Instructions ? PromptsType.instructions
Expand Down Expand Up @@ -1070,7 +1089,9 @@ async function renderEditor(ctx: ComponentFixtureContext, options: IRenderEditor
}());
reg.defineInstance(IExtensionService, new class extends mock<IExtensionService>() { }());
reg.defineInstance(ILanguageModelToolsService, new class extends mock<ILanguageModelToolsService>() {
override readonly toolSets = constObservable(options.emptyToolExtensions ? fixtureToolSets.filter(toolSet => toolSet.source.type !== 'extension') : fixtureToolSets);
override readonly toolSets = constObservable(options.emptyToolExtensions
? toolSets.filter(toolSet => toolSet.source.type !== 'extension')
: toolSets);
}());
const fixtureToolState: IToolEnablementState = { toolSets: new Map(), tools: new Map() };
reg.defineInstance(IAgentHostToolSetEnablementService, new class extends mock<IAgentHostToolSetEnablementService>() {
Expand All @@ -1087,7 +1108,7 @@ async function renderEditor(ctx: ComponentFixtureContext, options: IRenderEditor
override canExecuteOnSessionsWindow() { return true; }
}());
reg.defineInstance(IWorkbenchEnvironmentService, new class extends mock<IWorkbenchEnvironmentService>() {
override readonly isSessionsWindow = false;
override readonly isSessionsWindow = isSessionsWindow;
}());
reg.defineInstance(IQuickInputService, new class extends mock<IQuickInputService>() { }());
reg.defineInstance(IViewsService, new class extends mock<IViewsService>() {
Expand Down Expand Up @@ -2210,11 +2231,13 @@ export default defineThemedFixtureGroup({ path: 'chat/aiCustomizations/' }, {
}),

ToolsTabNarrow: defineComponentFixture({
labels: { kind: 'screenshot' },
labels: { kind: 'screenshot', blocksCi: true },
expectedVisualDescriptions: ['The narrow Agents-window Tools page shows Built-in Tools and an Extension Tools section with a count of eight.'],
render: ctx => renderEditor(ctx, {
sessionResource: localSessionResource,
sessionResource: agentHostCopilotSessionResource,
isSessionsWindow: true,
selectedSection: AICustomizationManagementSection.Tools,
availableHarnesses: [{ ...createVSCodeHarnessDescriptor(), hiddenSections: [] }],
toolSets: [...fixtureToolSets.filter(toolSet => toolSet.source.type !== 'extension'), ...overflowingExtensionToolSets],
managementSections: [
AICustomizationManagementSection.Agents,
AICustomizationManagementSection.Tools,
Expand Down
30 changes: 18 additions & 12 deletions test/componentFixtures/blocks-ci-screenshots.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@
![screenshot](https://hediet-screenshots.azurewebsites.net/images/3fe3102f05a571821d784b47042078b24862e554bae20c2b115908480754b748)

#### chat/aiCustomizations/aiCustomizationManagementEditor/AgentsTab/Dark
![screenshot](https://hediet-screenshots.azurewebsites.net/images/f36aea7db5a1512c8a8555ced070ddfba15c9c51fa2567c3078a7d487f864ef7)
![screenshot](https://hediet-screenshots.azurewebsites.net/images/68982febd7e0357bece0e9b8f059f8a90bf561b0c2a9d54145bdd65e1c06dac6)

#### chat/aiCustomizations/aiCustomizationManagementEditor/AgentsTab/Light
![screenshot](https://hediet-screenshots.azurewebsites.net/images/c7dd008ee794bf39d0e36a1f59dd737193b527fc066f0f31637335ea1be45e3a)
![screenshot](https://hediet-screenshots.azurewebsites.net/images/e6faf845e7514e0ee227da2b321752b3f73f83ab046db70d4ce94e4d0619539b)

#### chat/aiCustomizations/aiCustomizationManagementEditor/AgentsTabNarrow/Dark
![screenshot](https://hediet-screenshots.azurewebsites.net/images/2e92da016fb860020ebc223d034e77e09f866a89eff0311154150a50fe61d448)
![screenshot](https://hediet-screenshots.azurewebsites.net/images/99e091634cd3b67615fc291731c2e3798c5ee00dafd3e1a05d8f72a00a96614e)

#### chat/aiCustomizations/aiCustomizationManagementEditor/AgentsTabNarrow/Light
![screenshot](https://hediet-screenshots.azurewebsites.net/images/cfb5e9b34c4c0a45627f4ecedcfa05232876e84323a6b1aa23fba456f26efb19)
![screenshot](https://hediet-screenshots.azurewebsites.net/images/67d959f5f908ac8e098504eb33ae72d4d740ab7b366a664ca47ed1503bc91071)

#### chat/aiCustomizations/aiCustomizationManagementEditor/CodexSkillMissingDescriptionHover/Dark
![screenshot](https://hediet-screenshots.azurewebsites.net/images/77971ffe6fe53a03b1364f177c680657736aa2457ca4575cb477f4302165d180)
![screenshot](https://hediet-screenshots.azurewebsites.net/images/dff96e8a7c5eb7620587ba275576aabaf312e798a902ec39cc157f2bfd331d7b)

#### chat/aiCustomizations/aiCustomizationManagementEditor/CodexSkillMissingDescriptionHover/Light
![screenshot](https://hediet-screenshots.azurewebsites.net/images/f902ab091427376c2a0377323a1a2345131da95a600c5f7def854fdcf9596d4d)
![screenshot](https://hediet-screenshots.azurewebsites.net/images/bb1fb57e12079b464e4e4dcb6f0b3d660bb392ff6df88cbb83e161ef9a93516e)

#### chat/aiCustomizations/aiCustomizationManagementEditor/ConfiguredLocationsMigration/Dark
![screenshot](https://hediet-screenshots.azurewebsites.net/images/db8b3f8f69e3e0236cbca2b8997451761d288464151956cb8c1f578daec4d477)
Expand Down Expand Up @@ -103,22 +103,28 @@
![screenshot](https://hediet-screenshots.azurewebsites.net/images/9018471027ef940386ffb5524dc1126fb6d4b2b1a8f329775021b80fb9d0c6f5)

#### chat/aiCustomizations/aiCustomizationManagementEditor/PromptMigration/Dark
![screenshot](https://hediet-screenshots.azurewebsites.net/images/6ca2014821a31bc66819e22aaf350202b15560fb5cab6a7f3fa10318eb7a0b6d)
![screenshot](https://hediet-screenshots.azurewebsites.net/images/34688bda2976991524e669fd178bf4a6ce42f8a6ccf7c0a6070d813f7ca4ade9)

#### chat/aiCustomizations/aiCustomizationManagementEditor/PromptMigration/Light
![screenshot](https://hediet-screenshots.azurewebsites.net/images/2310ff42dec67e984854ab8f598e992ee2263b6347517a2d19e8ab0e9bae9b36)
![screenshot](https://hediet-screenshots.azurewebsites.net/images/b89939f490eddf6ad1e33c4d2b1504b9f683255bffa8a9b635d6a322e736215e)

#### chat/aiCustomizations/aiCustomizationManagementEditor/SessionsCodexSkillMissingDescriptionHover/Dark
![screenshot](https://hediet-screenshots.azurewebsites.net/images/77971ffe6fe53a03b1364f177c680657736aa2457ca4575cb477f4302165d180)
![screenshot](https://hediet-screenshots.azurewebsites.net/images/dff96e8a7c5eb7620587ba275576aabaf312e798a902ec39cc157f2bfd331d7b)

#### chat/aiCustomizations/aiCustomizationManagementEditor/SessionsCodexSkillMissingDescriptionHover/Light
![screenshot](https://hediet-screenshots.azurewebsites.net/images/f902ab091427376c2a0377323a1a2345131da95a600c5f7def854fdcf9596d4d)
![screenshot](https://hediet-screenshots.azurewebsites.net/images/bb1fb57e12079b464e4e4dcb6f0b3d660bb392ff6df88cbb83e161ef9a93516e)

#### chat/aiCustomizations/aiCustomizationManagementEditor/ToolsTab/Dark
![screenshot](https://hediet-screenshots.azurewebsites.net/images/42faa6cedc95315e848fef1bda477bc2317ca82b8f834ed43a2a92e2c083e429)
![screenshot](https://hediet-screenshots.azurewebsites.net/images/9d7bbf6ed8fb3362605d462032e3b1e67ce8ef963377a4b9a31845e529affd6b)

#### chat/aiCustomizations/aiCustomizationManagementEditor/ToolsTab/Light
![screenshot](https://hediet-screenshots.azurewebsites.net/images/a830564c5642147668cc525fd5143e54034b3c215a94ed064757768fb87b793a)
![screenshot](https://hediet-screenshots.azurewebsites.net/images/051a775907994881ced6f625c164c8401dbce9c8c3806416b08d126b64e2e7cc)

#### chat/aiCustomizations/aiCustomizationManagementEditor/ToolsTabNarrow/Dark
![screenshot](https://hediet-screenshots.azurewebsites.net/images/fac69c45f3ddc58b0ded3ec2579c573517cb21a43136187cf606cff730d58060)

#### chat/aiCustomizations/aiCustomizationManagementEditor/ToolsTabNarrow/Light
![screenshot](https://hediet-screenshots.azurewebsites.net/images/8100899ce8c0cf751385412fb51b55bbb246fafecf4ce38d83248b67a9bf7650)

#### chat/aiCustomizations/aiCustomizationManagementEditor/UserDataMigration/Dark
![screenshot](https://hediet-screenshots.azurewebsites.net/images/115ec4952191d77d6877fdd28e2b26f07218320d642b0ef6129f2244a0217b16)
Expand Down