mirror of
https://github.com/microsoft/vscode.git
synced 2026-02-14 23:18:36 +00:00
adds component explorer (#294075)
* adds component explorer * Removes log
This commit is contained in:
committed by
GitHub
parent
bde73caaee
commit
bce538863f
92
build/vite/fixtures/aiStats.fixture.ts
Normal file
92
build/vite/fixtures/aiStats.fixture.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { observableValue } from '../../../src/vs/base/common/observable';
|
||||
import { createAiStatsHover, IAiStatsHoverData } from '../../../src/vs/workbench/contrib/editTelemetry/browser/editStats/aiStatsStatusBar';
|
||||
import { ISessionData } from '../../../src/vs/workbench/contrib/editTelemetry/browser/editStats/aiStatsChart';
|
||||
import { Random } from '../../../src/vs/editor/test/common/core/random';
|
||||
import { ComponentFixtureContext, defineComponentFixture, defineThemedFixtureGroup } from './fixtureUtils';
|
||||
|
||||
export default defineThemedFixtureGroup({
|
||||
AiStatsHover: defineComponentFixture({
|
||||
render: (context) => renderAiStatsHover({ ...context, data: createSampleDataWithSessions() }),
|
||||
}),
|
||||
|
||||
AiStatsHoverNoData: defineComponentFixture({
|
||||
render: (context) => renderAiStatsHover({ ...context, data: createEmptyData() }),
|
||||
}),
|
||||
});
|
||||
|
||||
function createSampleDataWithSessions(): IAiStatsHoverData {
|
||||
const random = Random.create(42);
|
||||
|
||||
// Use a fixed base time for determinism (Jan 1, 2025, 12:00:00 UTC)
|
||||
const baseTime = 1735732800000;
|
||||
const dayMs = 24 * 60 * 60 * 1000;
|
||||
const sessionLengthMs = 5 * 60 * 1000;
|
||||
|
||||
// Generate fake session data for the last 7 days
|
||||
const fakeSessions: ISessionData[] = [];
|
||||
for (let day = 6; day >= 0; day--) {
|
||||
const dayStart = baseTime - day * dayMs;
|
||||
const sessionsPerDay = random.nextIntRange(3, 9);
|
||||
for (let s = 0; s < sessionsPerDay; s++) {
|
||||
const sessionTime = dayStart + s * sessionLengthMs * 2;
|
||||
fakeSessions.push({
|
||||
startTime: sessionTime,
|
||||
typedCharacters: random.nextIntRange(100, 600),
|
||||
aiCharacters: random.nextIntRange(200, 1000),
|
||||
acceptedInlineSuggestions: random.nextIntRange(1, 16),
|
||||
chatEditCount: random.nextIntRange(0, 5),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const totalAi = fakeSessions.reduce((sum, s) => sum + s.aiCharacters, 0);
|
||||
const totalTyped = fakeSessions.reduce((sum, s) => sum + s.typedCharacters, 0);
|
||||
const aiRate = totalAi / (totalAi + totalTyped);
|
||||
|
||||
// "Today" for the fixture is the baseTime day
|
||||
const startOfToday = baseTime - (baseTime % dayMs);
|
||||
const todaySessions = fakeSessions.filter(s => s.startTime >= startOfToday);
|
||||
const acceptedToday = todaySessions.reduce((sum, s) => sum + (s.acceptedInlineSuggestions ?? 0), 0);
|
||||
|
||||
return {
|
||||
aiRate: observableValue('aiRate', aiRate),
|
||||
acceptedInlineSuggestionsToday: observableValue('acceptedToday', acceptedToday),
|
||||
sessions: observableValue('sessions', fakeSessions),
|
||||
};
|
||||
}
|
||||
|
||||
function createEmptyData(): IAiStatsHoverData {
|
||||
return {
|
||||
aiRate: observableValue('aiRate', 0),
|
||||
acceptedInlineSuggestionsToday: observableValue('acceptedToday', 0),
|
||||
sessions: observableValue('sessions', []),
|
||||
};
|
||||
}
|
||||
|
||||
interface RenderAiStatsOptions extends ComponentFixtureContext {
|
||||
data: IAiStatsHoverData;
|
||||
}
|
||||
|
||||
function renderAiStatsHover({ container, disposableStore, data }: RenderAiStatsOptions): HTMLElement {
|
||||
container.style.width = '320px';
|
||||
container.style.padding = '8px';
|
||||
container.style.backgroundColor = 'var(--vscode-editorHoverWidget-background)';
|
||||
container.style.border = '1px solid var(--vscode-editorHoverWidget-border)';
|
||||
container.style.borderRadius = '4px';
|
||||
container.style.color = 'var(--vscode-editorHoverWidget-foreground)';
|
||||
|
||||
const hover = createAiStatsHover({
|
||||
data,
|
||||
onOpenSettings: () => console.log('Open settings clicked'),
|
||||
});
|
||||
|
||||
const elem = hover.keepUpdated(disposableStore).element;
|
||||
container.appendChild(elem);
|
||||
|
||||
return container;
|
||||
}
|
||||
531
build/vite/fixtures/baseUI.fixture.ts
Normal file
531
build/vite/fixtures/baseUI.fixture.ts
Normal file
@@ -0,0 +1,531 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { $ } from '../../../src/vs/base/browser/dom';
|
||||
import { Codicon } from '../../../src/vs/base/common/codicons';
|
||||
import { ThemeIcon } from '../../../src/vs/base/common/themables';
|
||||
import { Action, Separator } from '../../../src/vs/base/common/actions';
|
||||
|
||||
// UI Components
|
||||
import { Button, ButtonBar, ButtonWithDescription, unthemedButtonStyles } from '../../../src/vs/base/browser/ui/button/button';
|
||||
import { Toggle, Checkbox, unthemedToggleStyles } from '../../../src/vs/base/browser/ui/toggle/toggle';
|
||||
import { InputBox, MessageType, unthemedInboxStyles } from '../../../src/vs/base/browser/ui/inputbox/inputBox';
|
||||
import { CountBadge } from '../../../src/vs/base/browser/ui/countBadge/countBadge';
|
||||
import { ActionBar } from '../../../src/vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ProgressBar } from '../../../src/vs/base/browser/ui/progressbar/progressbar';
|
||||
import { HighlightedLabel } from '../../../src/vs/base/browser/ui/highlightedlabel/highlightedLabel';
|
||||
|
||||
import { ComponentFixtureContext, defineComponentFixture, defineThemedFixtureGroup } from './fixtureUtils';
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Styles (themed versions for fixture display)
|
||||
// ============================================================================
|
||||
|
||||
const themedButtonStyles = {
|
||||
...unthemedButtonStyles,
|
||||
buttonBackground: 'var(--vscode-button-background)',
|
||||
buttonHoverBackground: 'var(--vscode-button-hoverBackground)',
|
||||
buttonForeground: 'var(--vscode-button-foreground)',
|
||||
buttonSecondaryBackground: 'var(--vscode-button-secondaryBackground)',
|
||||
buttonSecondaryHoverBackground: 'var(--vscode-button-secondaryHoverBackground)',
|
||||
buttonSecondaryForeground: 'var(--vscode-button-secondaryForeground)',
|
||||
buttonBorder: 'var(--vscode-button-border)',
|
||||
};
|
||||
|
||||
const themedToggleStyles = {
|
||||
...unthemedToggleStyles,
|
||||
inputActiveOptionBorder: 'var(--vscode-inputOption-activeBorder)',
|
||||
inputActiveOptionForeground: 'var(--vscode-inputOption-activeForeground)',
|
||||
inputActiveOptionBackground: 'var(--vscode-inputOption-activeBackground)',
|
||||
};
|
||||
|
||||
const themedCheckboxStyles = {
|
||||
checkboxBackground: 'var(--vscode-checkbox-background)',
|
||||
checkboxBorder: 'var(--vscode-checkbox-border)',
|
||||
checkboxForeground: 'var(--vscode-checkbox-foreground)',
|
||||
checkboxDisabledBackground: undefined,
|
||||
checkboxDisabledForeground: undefined,
|
||||
};
|
||||
|
||||
const themedInputBoxStyles = {
|
||||
...unthemedInboxStyles,
|
||||
inputBackground: 'var(--vscode-input-background)',
|
||||
inputForeground: 'var(--vscode-input-foreground)',
|
||||
inputBorder: 'var(--vscode-input-border)',
|
||||
inputValidationInfoBackground: 'var(--vscode-inputValidation-infoBackground)',
|
||||
inputValidationInfoBorder: 'var(--vscode-inputValidation-infoBorder)',
|
||||
inputValidationWarningBackground: 'var(--vscode-inputValidation-warningBackground)',
|
||||
inputValidationWarningBorder: 'var(--vscode-inputValidation-warningBorder)',
|
||||
inputValidationErrorBackground: 'var(--vscode-inputValidation-errorBackground)',
|
||||
inputValidationErrorBorder: 'var(--vscode-inputValidation-errorBorder)',
|
||||
};
|
||||
|
||||
const themedBadgeStyles = {
|
||||
badgeBackground: 'var(--vscode-badge-background)',
|
||||
badgeForeground: 'var(--vscode-badge-foreground)',
|
||||
badgeBorder: undefined,
|
||||
};
|
||||
|
||||
const themedProgressBarOptions = {
|
||||
progressBarBackground: 'var(--vscode-progressBar-background)',
|
||||
};
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Buttons
|
||||
// ============================================================================
|
||||
|
||||
function renderButtons({ container, disposableStore }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
container.style.gap = '12px';
|
||||
|
||||
// Section: Primary Buttons
|
||||
const primarySection = $('div');
|
||||
primarySection.style.display = 'flex';
|
||||
primarySection.style.gap = '8px';
|
||||
primarySection.style.alignItems = 'center';
|
||||
container.appendChild(primarySection);
|
||||
|
||||
const primaryButton = disposableStore.add(new Button(primarySection, { ...themedButtonStyles, title: 'Primary button' }));
|
||||
primaryButton.label = 'Primary Button';
|
||||
|
||||
const primaryIconButton = disposableStore.add(new Button(primarySection, { ...themedButtonStyles, title: 'With Icon', supportIcons: true }));
|
||||
primaryIconButton.label = '$(add) Add Item';
|
||||
|
||||
const smallButton = disposableStore.add(new Button(primarySection, { ...themedButtonStyles, title: 'Small button', small: true }));
|
||||
smallButton.label = 'Small';
|
||||
|
||||
// Section: Secondary Buttons
|
||||
const secondarySection = $('div');
|
||||
secondarySection.style.display = 'flex';
|
||||
secondarySection.style.gap = '8px';
|
||||
secondarySection.style.alignItems = 'center';
|
||||
container.appendChild(secondarySection);
|
||||
|
||||
const secondaryButton = disposableStore.add(new Button(secondarySection, { ...themedButtonStyles, secondary: true, title: 'Secondary button' }));
|
||||
secondaryButton.label = 'Secondary Button';
|
||||
|
||||
const secondaryIconButton = disposableStore.add(new Button(secondarySection, { ...themedButtonStyles, secondary: true, title: 'Cancel', supportIcons: true }));
|
||||
secondaryIconButton.label = '$(close) Cancel';
|
||||
|
||||
// Section: Disabled Buttons
|
||||
const disabledSection = $('div');
|
||||
disabledSection.style.display = 'flex';
|
||||
disabledSection.style.gap = '8px';
|
||||
disabledSection.style.alignItems = 'center';
|
||||
container.appendChild(disabledSection);
|
||||
|
||||
const disabledButton = disposableStore.add(new Button(disabledSection, { ...themedButtonStyles, title: 'Disabled', disabled: true }));
|
||||
disabledButton.label = 'Disabled';
|
||||
disabledButton.enabled = false;
|
||||
|
||||
const disabledSecondary = disposableStore.add(new Button(disabledSection, { ...themedButtonStyles, secondary: true, title: 'Disabled Secondary', disabled: true }));
|
||||
disabledSecondary.label = 'Disabled Secondary';
|
||||
disabledSecondary.enabled = false;
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
function renderButtonBar({ container, disposableStore }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
container.style.gap = '16px';
|
||||
|
||||
// Button Bar
|
||||
const barContainer = $('div');
|
||||
container.appendChild(barContainer);
|
||||
|
||||
const buttonBar = new ButtonBar(barContainer);
|
||||
disposableStore.add(buttonBar);
|
||||
|
||||
const okButton = buttonBar.addButton({ ...themedButtonStyles, title: 'OK' });
|
||||
okButton.label = 'OK';
|
||||
|
||||
const cancelButton = buttonBar.addButton({ ...themedButtonStyles, secondary: true, title: 'Cancel' });
|
||||
cancelButton.label = 'Cancel';
|
||||
|
||||
// Button with Description
|
||||
const descContainer = $('div');
|
||||
descContainer.style.width = '300px';
|
||||
container.appendChild(descContainer);
|
||||
|
||||
const buttonWithDesc = disposableStore.add(new ButtonWithDescription(descContainer, { ...themedButtonStyles, title: 'Install Extension', supportIcons: true }));
|
||||
buttonWithDesc.label = '$(extensions) Install Extension';
|
||||
buttonWithDesc.description = 'This will install the extension and enable it globally';
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Toggles and Checkboxes
|
||||
// ============================================================================
|
||||
|
||||
function renderToggles({ container, disposableStore }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
container.style.gap = '12px';
|
||||
|
||||
// Toggles
|
||||
const toggleSection = $('div');
|
||||
toggleSection.style.display = 'flex';
|
||||
toggleSection.style.gap = '16px';
|
||||
toggleSection.style.alignItems = 'center';
|
||||
container.appendChild(toggleSection);
|
||||
|
||||
const toggle1 = disposableStore.add(new Toggle({
|
||||
...themedToggleStyles,
|
||||
title: 'Case Sensitive',
|
||||
isChecked: false,
|
||||
icon: Codicon.caseSensitive,
|
||||
}));
|
||||
toggleSection.appendChild(toggle1.domNode);
|
||||
|
||||
const toggle2 = disposableStore.add(new Toggle({
|
||||
...themedToggleStyles,
|
||||
title: 'Whole Word',
|
||||
isChecked: true,
|
||||
icon: Codicon.wholeWord,
|
||||
}));
|
||||
toggleSection.appendChild(toggle2.domNode);
|
||||
|
||||
const toggle3 = disposableStore.add(new Toggle({
|
||||
...themedToggleStyles,
|
||||
title: 'Use Regular Expression',
|
||||
isChecked: false,
|
||||
icon: Codicon.regex,
|
||||
}));
|
||||
toggleSection.appendChild(toggle3.domNode);
|
||||
|
||||
// Checkboxes
|
||||
const checkboxSection = $('div');
|
||||
checkboxSection.style.display = 'flex';
|
||||
checkboxSection.style.flexDirection = 'column';
|
||||
checkboxSection.style.gap = '8px';
|
||||
container.appendChild(checkboxSection);
|
||||
|
||||
const createCheckboxRow = (label: string, checked: boolean) => {
|
||||
const row = $('div');
|
||||
row.style.display = 'flex';
|
||||
row.style.alignItems = 'center';
|
||||
row.style.gap = '8px';
|
||||
|
||||
const checkbox = disposableStore.add(new Checkbox(label, checked, themedCheckboxStyles));
|
||||
row.appendChild(checkbox.domNode);
|
||||
|
||||
const labelEl = $('span');
|
||||
labelEl.textContent = label;
|
||||
labelEl.style.color = 'var(--vscode-foreground)';
|
||||
row.appendChild(labelEl);
|
||||
|
||||
return row;
|
||||
};
|
||||
|
||||
checkboxSection.appendChild(createCheckboxRow('Enable auto-save', true));
|
||||
checkboxSection.appendChild(createCheckboxRow('Show line numbers', true));
|
||||
checkboxSection.appendChild(createCheckboxRow('Word wrap', false));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Input Boxes
|
||||
// ============================================================================
|
||||
|
||||
function renderInputBoxes({ container, disposableStore }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
container.style.gap = '16px';
|
||||
container.style.width = '350px';
|
||||
|
||||
// Normal input
|
||||
const normalInput = disposableStore.add(new InputBox(container, undefined, {
|
||||
placeholder: 'Enter search query...',
|
||||
inputBoxStyles: themedInputBoxStyles,
|
||||
}));
|
||||
|
||||
// Input with value
|
||||
const filledInput = disposableStore.add(new InputBox(container, undefined, {
|
||||
placeholder: 'File path',
|
||||
inputBoxStyles: themedInputBoxStyles,
|
||||
}));
|
||||
filledInput.value = '/src/vs/editor/browser';
|
||||
|
||||
// Input with info validation
|
||||
const infoInput = disposableStore.add(new InputBox(container, undefined, {
|
||||
placeholder: 'Username',
|
||||
inputBoxStyles: themedInputBoxStyles,
|
||||
validationOptions: {
|
||||
validation: (value) => value.length < 3 ? { content: 'Username must be at least 3 characters', type: MessageType.INFO } : null
|
||||
}
|
||||
}));
|
||||
infoInput.value = 'ab';
|
||||
infoInput.validate();
|
||||
|
||||
// Input with warning validation
|
||||
const warningInput = disposableStore.add(new InputBox(container, undefined, {
|
||||
placeholder: 'Password',
|
||||
inputBoxStyles: themedInputBoxStyles,
|
||||
validationOptions: {
|
||||
validation: (value) => value.length < 8 ? { content: 'Password should be at least 8 characters for security', type: MessageType.WARNING } : null
|
||||
}
|
||||
}));
|
||||
warningInput.value = 'pass';
|
||||
warningInput.validate();
|
||||
|
||||
// Input with error validation
|
||||
const errorInput = disposableStore.add(new InputBox(container, undefined, {
|
||||
placeholder: 'Email address',
|
||||
inputBoxStyles: themedInputBoxStyles,
|
||||
validationOptions: {
|
||||
validation: (value) => !value.includes('@') ? { content: 'Please enter a valid email address', type: MessageType.ERROR } : null
|
||||
}
|
||||
}));
|
||||
errorInput.value = 'invalid-email';
|
||||
errorInput.validate();
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Count Badges
|
||||
// ============================================================================
|
||||
|
||||
function renderCountBadges({ container }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.gap = '12px';
|
||||
container.style.alignItems = 'center';
|
||||
|
||||
// Various badge counts
|
||||
const counts = [1, 5, 12, 99, 999];
|
||||
|
||||
for (const count of counts) {
|
||||
const badgeContainer = $('div');
|
||||
badgeContainer.style.display = 'flex';
|
||||
badgeContainer.style.alignItems = 'center';
|
||||
badgeContainer.style.gap = '8px';
|
||||
|
||||
const label = $('span');
|
||||
label.textContent = 'Issues';
|
||||
label.style.color = 'var(--vscode-foreground)';
|
||||
badgeContainer.appendChild(label);
|
||||
|
||||
new CountBadge(badgeContainer, { count }, themedBadgeStyles);
|
||||
container.appendChild(badgeContainer);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Action Bar
|
||||
// ============================================================================
|
||||
|
||||
function renderActionBar({ container, disposableStore }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
container.style.gap = '16px';
|
||||
|
||||
// Horizontal action bar
|
||||
const horizontalLabel = $('div');
|
||||
horizontalLabel.textContent = 'Horizontal Actions:';
|
||||
horizontalLabel.style.color = 'var(--vscode-foreground)';
|
||||
horizontalLabel.style.marginBottom = '4px';
|
||||
container.appendChild(horizontalLabel);
|
||||
|
||||
const horizontalContainer = $('div');
|
||||
container.appendChild(horizontalContainer);
|
||||
|
||||
const horizontalBar = disposableStore.add(new ActionBar(horizontalContainer, {
|
||||
ariaLabel: 'Editor Actions',
|
||||
}));
|
||||
|
||||
horizontalBar.push([
|
||||
new Action('editor.action.save', 'Save', ThemeIcon.asClassName(Codicon.save), true, async () => console.log('Save')),
|
||||
new Action('editor.action.undo', 'Undo', ThemeIcon.asClassName(Codicon.discard), true, async () => console.log('Undo')),
|
||||
new Action('editor.action.redo', 'Redo', ThemeIcon.asClassName(Codicon.redo), true, async () => console.log('Redo')),
|
||||
new Separator(),
|
||||
new Action('editor.action.find', 'Find', ThemeIcon.asClassName(Codicon.search), true, async () => console.log('Find')),
|
||||
new Action('editor.action.replace', 'Replace', ThemeIcon.asClassName(Codicon.replaceAll), true, async () => console.log('Replace')),
|
||||
]);
|
||||
|
||||
// Action bar with disabled items
|
||||
const mixedLabel = $('div');
|
||||
mixedLabel.textContent = 'Mixed States:';
|
||||
mixedLabel.style.color = 'var(--vscode-foreground)';
|
||||
mixedLabel.style.marginBottom = '4px';
|
||||
container.appendChild(mixedLabel);
|
||||
|
||||
const mixedContainer = $('div');
|
||||
container.appendChild(mixedContainer);
|
||||
|
||||
const mixedBar = disposableStore.add(new ActionBar(mixedContainer, {
|
||||
ariaLabel: 'Mixed Actions',
|
||||
}));
|
||||
|
||||
mixedBar.push([
|
||||
new Action('action.enabled', 'Enabled', ThemeIcon.asClassName(Codicon.play), true, async () => { }),
|
||||
new Action('action.disabled', 'Disabled', ThemeIcon.asClassName(Codicon.debugPause), false, async () => { }),
|
||||
new Action('action.enabled2', 'Enabled', ThemeIcon.asClassName(Codicon.debugStop), true, async () => { }),
|
||||
]);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Progress Bar
|
||||
// ============================================================================
|
||||
|
||||
function renderProgressBars({ container, disposableStore }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
container.style.gap = '24px';
|
||||
container.style.width = '400px';
|
||||
|
||||
const createSection = (label: string) => {
|
||||
const section = $('div');
|
||||
const labelEl = $('div');
|
||||
labelEl.textContent = label;
|
||||
labelEl.style.color = 'var(--vscode-foreground)';
|
||||
labelEl.style.marginBottom = '8px';
|
||||
labelEl.style.fontSize = '12px';
|
||||
section.appendChild(labelEl);
|
||||
|
||||
// Progress bar container with proper constraints
|
||||
const barContainer = $('div');
|
||||
barContainer.style.position = 'relative';
|
||||
barContainer.style.width = '100%';
|
||||
barContainer.style.height = '4px';
|
||||
barContainer.style.overflow = 'hidden';
|
||||
section.appendChild(barContainer);
|
||||
|
||||
container.appendChild(section);
|
||||
return barContainer;
|
||||
};
|
||||
|
||||
// Infinite progress
|
||||
const infiniteSection = createSection('Infinite Progress (loading...)');
|
||||
const infiniteBar = disposableStore.add(new ProgressBar(infiniteSection, themedProgressBarOptions));
|
||||
infiniteBar.infinite();
|
||||
|
||||
// Discrete progress - 30%
|
||||
const progress30Section = createSection('Discrete Progress - 30%');
|
||||
const progress30Bar = disposableStore.add(new ProgressBar(progress30Section, themedProgressBarOptions));
|
||||
progress30Bar.total(100);
|
||||
progress30Bar.worked(30);
|
||||
|
||||
// Discrete progress - 60%
|
||||
const progress60Section = createSection('Discrete Progress - 60%');
|
||||
const progress60Bar = disposableStore.add(new ProgressBar(progress60Section, themedProgressBarOptions));
|
||||
progress60Bar.total(100);
|
||||
progress60Bar.worked(60);
|
||||
|
||||
// Discrete progress - 90%
|
||||
const progress90Section = createSection('Discrete Progress - 90%');
|
||||
const progress90Bar = disposableStore.add(new ProgressBar(progress90Section, themedProgressBarOptions));
|
||||
progress90Bar.total(100);
|
||||
progress90Bar.worked(90);
|
||||
|
||||
// Completed progress
|
||||
const doneSection = createSection('Completed (100%)');
|
||||
const doneBar = disposableStore.add(new ProgressBar(doneSection, themedProgressBarOptions));
|
||||
doneBar.total(100);
|
||||
doneBar.worked(100);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Highlighted Label
|
||||
// ============================================================================
|
||||
|
||||
function renderHighlightedLabels({ container }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
container.style.gap = '8px';
|
||||
container.style.color = 'var(--vscode-foreground)';
|
||||
|
||||
const createHighlightedLabel = (text: string, highlights: { start: number; end: number }[]) => {
|
||||
const row = $('div');
|
||||
row.style.display = 'flex';
|
||||
row.style.alignItems = 'center';
|
||||
row.style.gap = '8px';
|
||||
|
||||
const labelContainer = $('div');
|
||||
const label = new HighlightedLabel(labelContainer);
|
||||
label.set(text, highlights);
|
||||
row.appendChild(labelContainer);
|
||||
|
||||
const queryLabel = $('span');
|
||||
queryLabel.style.color = 'var(--vscode-descriptionForeground)';
|
||||
queryLabel.style.fontSize = '12px';
|
||||
queryLabel.textContent = `(matches highlighted)`;
|
||||
row.appendChild(queryLabel);
|
||||
|
||||
return row;
|
||||
};
|
||||
|
||||
// File search examples
|
||||
container.appendChild(createHighlightedLabel('codeEditorWidget.ts', [{ start: 0, end: 4 }])); // "code"
|
||||
container.appendChild(createHighlightedLabel('inlineCompletionsController.ts', [{ start: 6, end: 10 }])); // "Comp"
|
||||
container.appendChild(createHighlightedLabel('diffEditorViewModel.ts', [{ start: 0, end: 4 }, { start: 10, end: 14 }])); // "diff" and "View"
|
||||
container.appendChild(createHighlightedLabel('workbenchTestServices.ts', [{ start: 9, end: 13 }])); // "Test"
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Export Fixtures
|
||||
// ============================================================================
|
||||
|
||||
export default defineThemedFixtureGroup({
|
||||
Buttons: defineComponentFixture({
|
||||
render: renderButtons,
|
||||
}),
|
||||
|
||||
ButtonBar: defineComponentFixture({
|
||||
render: renderButtonBar,
|
||||
}),
|
||||
|
||||
Toggles: defineComponentFixture({
|
||||
render: renderToggles,
|
||||
}),
|
||||
|
||||
InputBoxes: defineComponentFixture({
|
||||
render: renderInputBoxes,
|
||||
}),
|
||||
|
||||
CountBadges: defineComponentFixture({
|
||||
render: renderCountBadges,
|
||||
}),
|
||||
|
||||
ActionBar: defineComponentFixture({
|
||||
render: renderActionBar,
|
||||
}),
|
||||
|
||||
ProgressBars: defineComponentFixture({
|
||||
render: renderProgressBars,
|
||||
}),
|
||||
|
||||
HighlightedLabels: defineComponentFixture({
|
||||
render: renderHighlightedLabels,
|
||||
}),
|
||||
});
|
||||
76
build/vite/fixtures/editor/codeEditor.fixture.ts
Normal file
76
build/vite/fixtures/editor/codeEditor.fixture.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI } from '../../../../src/vs/base/common/uri';
|
||||
import { CodeEditorWidget, ICodeEditorWidgetOptions } from '../../../../src/vs/editor/browser/widget/codeEditor/codeEditorWidget';
|
||||
import { ComponentFixtureContext, createEditorServices, createTextModel, defineComponentFixture, defineThemedFixtureGroup } from '../fixtureUtils';
|
||||
|
||||
const SAMPLE_CODE = `// Welcome to VS Code
|
||||
function greet(name: string): string {
|
||||
return \`Hello, \${name}!\`;
|
||||
}
|
||||
|
||||
class Counter {
|
||||
private _count = 0;
|
||||
|
||||
increment(): void {
|
||||
this._count++;
|
||||
}
|
||||
|
||||
get count(): number {
|
||||
return this._count;
|
||||
}
|
||||
}
|
||||
|
||||
const counter = new Counter();
|
||||
counter.increment();
|
||||
console.log(greet('World'));
|
||||
console.log(\`Count: \${counter.count}\`);
|
||||
`;
|
||||
|
||||
function renderCodeEditor({ container, disposableStore, theme }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.width = '600px';
|
||||
container.style.height = '400px';
|
||||
container.style.border = '1px solid var(--vscode-editorWidget-border)';
|
||||
|
||||
const instantiationService = createEditorServices(disposableStore, { colorTheme: theme });
|
||||
|
||||
const model = disposableStore.add(createTextModel(
|
||||
instantiationService,
|
||||
SAMPLE_CODE,
|
||||
URI.parse('inmemory://sample.ts'),
|
||||
'typescript'
|
||||
));
|
||||
|
||||
const editorOptions: ICodeEditorWidgetOptions = {
|
||||
contributions: []
|
||||
};
|
||||
|
||||
const editor = disposableStore.add(instantiationService.createInstance(
|
||||
CodeEditorWidget,
|
||||
container,
|
||||
{
|
||||
automaticLayout: true,
|
||||
minimap: { enabled: true },
|
||||
lineNumbers: 'on',
|
||||
scrollBeyondLastLine: false,
|
||||
fontSize: 14,
|
||||
fontFamily: 'Consolas, "Courier New", monospace',
|
||||
renderWhitespace: 'selection',
|
||||
bracketPairColorization: { enabled: true },
|
||||
},
|
||||
editorOptions
|
||||
));
|
||||
|
||||
editor.setModel(model);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
export default defineThemedFixtureGroup({
|
||||
CodeEditor: defineComponentFixture({
|
||||
render: (context) => renderCodeEditor(context),
|
||||
}),
|
||||
});
|
||||
164
build/vite/fixtures/editor/inlineCompletions.fixture.ts
Normal file
164
build/vite/fixtures/editor/inlineCompletions.fixture.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { constObservable } from '../../../../src/vs/base/common/observable';
|
||||
import { URI } from '../../../../src/vs/base/common/uri';
|
||||
import { Range } from '../../../../src/vs/editor/common/core/range';
|
||||
import { IEditorOptions } from '../../../../src/vs/editor/common/config/editorOptions';
|
||||
import { CodeEditorWidget, ICodeEditorWidgetOptions } from '../../../../src/vs/editor/browser/widget/codeEditor/codeEditorWidget';
|
||||
import { EditorExtensionsRegistry } from '../../../../src/vs/editor/browser/editorExtensions';
|
||||
import { InlineCompletionsController } from '../../../../src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController';
|
||||
import { InlineCompletionsSource, InlineCompletionsState } from '../../../../src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource';
|
||||
import { InlineEditItem } from '../../../../src/vs/editor/contrib/inlineCompletions/browser/model/inlineSuggestionItem';
|
||||
import { TextModelValueReference } from '../../../../src/vs/editor/contrib/inlineCompletions/browser/model/textModelValueReference';
|
||||
import { ComponentFixtureContext, createEditorServices, createTextModel, defineComponentFixture, defineThemedFixtureGroup } from '../fixtureUtils';
|
||||
|
||||
// Import to register the inline completions contribution
|
||||
import '../../../../src/vs/editor/contrib/inlineCompletions/browser/inlineCompletions.contribution';
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Inline Edit Fixture
|
||||
// ============================================================================
|
||||
|
||||
interface InlineEditOptions extends ComponentFixtureContext {
|
||||
code: string;
|
||||
cursorLine: number;
|
||||
range: { startLineNumber: number; startColumn: number; endLineNumber: number; endColumn: number };
|
||||
newText: string;
|
||||
width?: string;
|
||||
height?: string;
|
||||
editorOptions?: IEditorOptions;
|
||||
}
|
||||
|
||||
function renderInlineEdit(options: InlineEditOptions): HTMLElement {
|
||||
const { container, disposableStore, theme } = options;
|
||||
container.style.width = options.width ?? '500px';
|
||||
container.style.height = options.height ?? '170px';
|
||||
container.style.border = '1px solid var(--vscode-editorWidget-border)';
|
||||
|
||||
const instantiationService = createEditorServices(disposableStore, { colorTheme: theme });
|
||||
|
||||
const textModel = disposableStore.add(createTextModel(
|
||||
instantiationService,
|
||||
options.code,
|
||||
URI.parse('inmemory://inline-edit.ts'),
|
||||
'typescript'
|
||||
));
|
||||
|
||||
// Mock the InlineCompletionsSource to provide our test completion
|
||||
instantiationService.stubInstance(InlineCompletionsSource, {
|
||||
cancelUpdate: () => { },
|
||||
clear: () => { },
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
clearOperationOnTextModelChange: constObservable(undefined) as any,
|
||||
clearSuggestWidgetInlineCompletions: () => { },
|
||||
dispose: () => { },
|
||||
fetch: async () => true,
|
||||
inlineCompletions: constObservable(new InlineCompletionsState([
|
||||
InlineEditItem.createForTest(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
TextModelValueReference.snapshot(textModel as any),
|
||||
new Range(
|
||||
options.range.startLineNumber,
|
||||
options.range.startColumn,
|
||||
options.range.endLineNumber,
|
||||
options.range.endColumn
|
||||
),
|
||||
options.newText
|
||||
)
|
||||
], undefined)),
|
||||
loading: constObservable(false),
|
||||
seedInlineCompletionsWithSuggestWidget: () => { },
|
||||
seedWithCompletion: () => { },
|
||||
suggestWidgetInlineCompletions: constObservable(InlineCompletionsState.createEmpty()),
|
||||
});
|
||||
|
||||
const editorWidgetOptions: ICodeEditorWidgetOptions = {
|
||||
contributions: EditorExtensionsRegistry.getEditorContributions()
|
||||
};
|
||||
|
||||
const editor = disposableStore.add(instantiationService.createInstance(
|
||||
CodeEditorWidget,
|
||||
container,
|
||||
{
|
||||
automaticLayout: true,
|
||||
minimap: { enabled: false },
|
||||
lineNumbers: 'on',
|
||||
scrollBeyondLastLine: false,
|
||||
fontSize: 14,
|
||||
cursorBlinking: 'solid',
|
||||
...options.editorOptions,
|
||||
},
|
||||
editorWidgetOptions
|
||||
));
|
||||
|
||||
editor.setModel(textModel);
|
||||
editor.setPosition({ lineNumber: options.cursorLine, column: 1 });
|
||||
editor.focus();
|
||||
|
||||
// Trigger inline completions
|
||||
const controller = InlineCompletionsController.get(editor);
|
||||
controller?.model?.get();
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Fixtures
|
||||
// ============================================================================
|
||||
|
||||
export default defineThemedFixtureGroup({
|
||||
// Side-by-side view: Multi-line replacement
|
||||
SideBySideView: defineComponentFixture({
|
||||
render: (context) => renderInlineEdit({
|
||||
...context,
|
||||
code: `function greet(name) {
|
||||
console.log("Hello, " + name);
|
||||
}`,
|
||||
cursorLine: 2,
|
||||
range: { startLineNumber: 2, startColumn: 1, endLineNumber: 2, endColumn: 100 },
|
||||
newText: ' console.log(`Hello, ${name}!`);',
|
||||
}),
|
||||
}),
|
||||
|
||||
// Word replacement view: Single word change
|
||||
WordReplacementView: defineComponentFixture({
|
||||
render: (context) => renderInlineEdit({
|
||||
...context,
|
||||
code: `class BufferData {
|
||||
append(data: number[]) {
|
||||
this.data.push(data);
|
||||
}
|
||||
}`,
|
||||
cursorLine: 2,
|
||||
range: { startLineNumber: 2, startColumn: 2, endLineNumber: 2, endColumn: 8 },
|
||||
newText: 'push',
|
||||
height: '200px',
|
||||
}),
|
||||
}),
|
||||
|
||||
// Insertion view: Insert new content
|
||||
InsertionView: defineComponentFixture({
|
||||
render: (context) => renderInlineEdit({
|
||||
...context,
|
||||
code: `class BufferData {
|
||||
append(data: number[]) {} // appends data
|
||||
}`,
|
||||
cursorLine: 2,
|
||||
range: { startLineNumber: 2, startColumn: 26, endLineNumber: 2, endColumn: 26 },
|
||||
newText: `
|
||||
console.log(data);
|
||||
`,
|
||||
height: '200px',
|
||||
editorOptions: {
|
||||
inlineSuggest: {
|
||||
edits: { allowCodeShifting: 'always' }
|
||||
}
|
||||
}
|
||||
}),
|
||||
}),
|
||||
});
|
||||
512
build/vite/fixtures/fixtureUtils.ts
Normal file
512
build/vite/fixtures/fixtureUtils.ts
Normal file
@@ -0,0 +1,512 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { defineFixture, defineFixtureGroup, defineFixtureVariants } from '@vscode/component-explorer';
|
||||
import { DisposableStore, toDisposable } from '../../../src/vs/base/common/lifecycle';
|
||||
import { URI } from '../../../src/vs/base/common/uri';
|
||||
import '../style.css';
|
||||
|
||||
// Theme
|
||||
import { COLOR_THEME_DARK_INITIAL_COLORS, COLOR_THEME_LIGHT_INITIAL_COLORS } from '../../../src/vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { ColorThemeData } from '../../../src/vs/workbench/services/themes/common/colorThemeData';
|
||||
import { ColorScheme } from '../../../src/vs/platform/theme/common/theme';
|
||||
import { generateColorThemeCSS } from '../../../src/vs/workbench/services/themes/browser/colorThemeCss';
|
||||
import { Registry } from '../../../src/vs/platform/registry/common/platform';
|
||||
import { Extensions as ThemingExtensions, IThemingRegistry } from '../../../src/vs/platform/theme/common/themeService';
|
||||
import { IEnvironmentService } from '../../../src/vs/platform/environment/common/environment';
|
||||
import { getIconsStyleSheet } from '../../../src/vs/platform/theme/browser/iconsStyleSheet';
|
||||
|
||||
// Instantiation
|
||||
import { ServiceCollection } from '../../../src/vs/platform/instantiation/common/serviceCollection';
|
||||
import { SyncDescriptor } from '../../../src/vs/platform/instantiation/common/descriptors';
|
||||
import { ServiceIdentifier } from '../../../src/vs/platform/instantiation/common/instantiation';
|
||||
import { TestInstantiationService } from '../../../src/vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
|
||||
// Test service implementations
|
||||
import { TestAccessibilityService } from '../../../src/vs/platform/accessibility/test/common/testAccessibilityService';
|
||||
import { MockKeybindingService, MockContextKeyService } from '../../../src/vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
import { TestClipboardService } from '../../../src/vs/platform/clipboard/test/common/testClipboardService';
|
||||
import { TestEditorWorkerService } from '../../../src/vs/editor/test/common/services/testEditorWorkerService';
|
||||
import { NullOpenerService } from '../../../src/vs/platform/opener/test/common/nullOpenerService';
|
||||
import { TestNotificationService } from '../../../src/vs/platform/notification/test/common/testNotificationService';
|
||||
import { TestDialogService } from '../../../src/vs/platform/dialogs/test/common/testDialogService';
|
||||
import { TestConfigurationService } from '../../../src/vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { TestTextResourcePropertiesService } from '../../../src/vs/editor/test/common/services/testTextResourcePropertiesService';
|
||||
import { TestThemeService } from '../../../src/vs/platform/theme/test/common/testThemeService';
|
||||
import { TestLanguageConfigurationService } from '../../../src/vs/editor/test/common/modes/testLanguageConfigurationService';
|
||||
import { TestCodeEditorService, TestCommandService } from '../../../src/vs/editor/test/browser/editorTestServices';
|
||||
import { TestTreeSitterLibraryService } from '../../../src/vs/editor/test/common/services/testTreeSitterLibraryService';
|
||||
import { TestMenuService } from '../../../src/vs/workbench/test/browser/workbenchTestServices';
|
||||
|
||||
// Service interfaces
|
||||
import { IAccessibilityService } from '../../../src/vs/platform/accessibility/common/accessibility';
|
||||
import { IKeybindingService } from '../../../src/vs/platform/keybinding/common/keybinding';
|
||||
import { IClipboardService } from '../../../src/vs/platform/clipboard/common/clipboardService';
|
||||
import { IEditorWorkerService } from '../../../src/vs/editor/common/services/editorWorker';
|
||||
import { IOpenerService } from '../../../src/vs/platform/opener/common/opener';
|
||||
import { INotificationService } from '../../../src/vs/platform/notification/common/notification';
|
||||
import { IDialogService } from '../../../src/vs/platform/dialogs/common/dialogs';
|
||||
import { IUndoRedoService } from '../../../src/vs/platform/undoRedo/common/undoRedo';
|
||||
import { UndoRedoService } from '../../../src/vs/platform/undoRedo/common/undoRedoService';
|
||||
import { ILanguageService } from '../../../src/vs/editor/common/languages/language';
|
||||
import { LanguageService } from '../../../src/vs/editor/common/services/languageService';
|
||||
import { ILanguageConfigurationService } from '../../../src/vs/editor/common/languages/languageConfigurationRegistry';
|
||||
import { IConfigurationService } from '../../../src/vs/platform/configuration/common/configuration';
|
||||
import { ITextResourcePropertiesService } from '../../../src/vs/editor/common/services/textResourceConfiguration';
|
||||
import { IColorTheme, IThemeService } from '../../../src/vs/platform/theme/common/themeService';
|
||||
import { ILogService, NullLogService, ILoggerService, NullLoggerService } from '../../../src/vs/platform/log/common/log';
|
||||
import { IModelService } from '../../../src/vs/editor/common/services/model';
|
||||
import { ModelService } from '../../../src/vs/editor/common/services/modelService';
|
||||
import { ICodeEditorService } from '../../../src/vs/editor/browser/services/codeEditorService';
|
||||
import { IContextKeyService } from '../../../src/vs/platform/contextkey/common/contextkey';
|
||||
import { ICommandService } from '../../../src/vs/platform/commands/common/commands';
|
||||
import { ITelemetryService } from '../../../src/vs/platform/telemetry/common/telemetry';
|
||||
import { NullTelemetryServiceShape } from '../../../src/vs/platform/telemetry/common/telemetryUtils';
|
||||
import { ILanguageFeatureDebounceService, LanguageFeatureDebounceService } from '../../../src/vs/editor/common/services/languageFeatureDebounce';
|
||||
import { ILanguageFeaturesService } from '../../../src/vs/editor/common/services/languageFeatures';
|
||||
import { LanguageFeaturesService } from '../../../src/vs/editor/common/services/languageFeaturesService';
|
||||
import { ITreeSitterLibraryService } from '../../../src/vs/editor/common/services/treeSitter/treeSitterLibraryService';
|
||||
import { IInlineCompletionsService, InlineCompletionsService } from '../../../src/vs/editor/browser/services/inlineCompletionsService';
|
||||
import { ICodeLensCache } from '../../../src/vs/editor/contrib/codelens/browser/codeLensCache';
|
||||
import { IHoverService } from '../../../src/vs/platform/hover/browser/hover';
|
||||
import { IDataChannelService, NullDataChannelService } from '../../../src/vs/platform/dataChannel/common/dataChannel';
|
||||
import { IContextMenuService, IContextViewService } from '../../../src/vs/platform/contextview/browser/contextView';
|
||||
import { ILabelService } from '../../../src/vs/platform/label/common/label';
|
||||
import { IMenuService } from '../../../src/vs/platform/actions/common/actions';
|
||||
import { IActionViewItemService, NullActionViewItemService } from '../../../src/vs/platform/actions/browser/actionViewItemService';
|
||||
import { IDefaultAccountService } from '../../../src/vs/platform/defaultAccount/common/defaultAccount';
|
||||
import { IStorageService, IStorageValueChangeEvent, IWillSaveStateEvent, StorageScope, StorageTarget, IStorageTargetChangeEvent, IStorageEntry, WillSaveStateReason, IWorkspaceStorageValueChangeEvent, IProfileStorageValueChangeEvent, IApplicationStorageValueChangeEvent } from '../../../src/vs/platform/storage/common/storage';
|
||||
import { Emitter, Event } from '../../../src/vs/base/common/event';
|
||||
import { mock } from '../../../src/vs/base/test/common/mock';
|
||||
import { IAnyWorkspaceIdentifier } from '../../../src/vs/platform/workspace/common/workspace';
|
||||
import { IUserDataProfile } from '../../../src/vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { IUserInteractionService, MockUserInteractionService } from '../../../src/vs/platform/userInteraction/browser/userInteractionService';
|
||||
|
||||
// Editor
|
||||
import { ITextModel } from '../../../src/vs/editor/common/model';
|
||||
|
||||
|
||||
|
||||
// Import color registrations to ensure colors are available
|
||||
import '../../../src/vs/platform/theme/common/colors/baseColors';
|
||||
import '../../../src/vs/platform/theme/common/colors/editorColors';
|
||||
import '../../../src/vs/platform/theme/common/colors/listColors';
|
||||
import '../../../src/vs/platform/theme/common/colors/miscColors';
|
||||
import '../../../src/vs/workbench/common/theme';
|
||||
|
||||
/**
|
||||
* A storage service that never stores anything and always returns the default/fallback value.
|
||||
* This is useful for fixtures where we want consistent behavior without persisted state.
|
||||
*/
|
||||
class NullStorageService implements IStorageService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private readonly _onDidChangeValue = new Emitter<IStorageValueChangeEvent>();
|
||||
onDidChangeValue(scope: StorageScope.WORKSPACE, key: string | undefined, disposable: DisposableStore): Event<IWorkspaceStorageValueChangeEvent>;
|
||||
onDidChangeValue(scope: StorageScope.PROFILE, key: string | undefined, disposable: DisposableStore): Event<IProfileStorageValueChangeEvent>;
|
||||
onDidChangeValue(scope: StorageScope.APPLICATION, key: string | undefined, disposable: DisposableStore): Event<IApplicationStorageValueChangeEvent>;
|
||||
onDidChangeValue(scope: StorageScope, key: string | undefined, disposable: DisposableStore): Event<IStorageValueChangeEvent> {
|
||||
return Event.filter(this._onDidChangeValue.event, e => e.scope === scope && (key === undefined || e.key === key), disposable);
|
||||
}
|
||||
|
||||
private readonly _onDidChangeTarget = new Emitter<IStorageTargetChangeEvent>();
|
||||
readonly onDidChangeTarget: Event<IStorageTargetChangeEvent> = this._onDidChangeTarget.event;
|
||||
|
||||
private readonly _onWillSaveState = new Emitter<IWillSaveStateEvent>();
|
||||
readonly onWillSaveState: Event<IWillSaveStateEvent> = this._onWillSaveState.event;
|
||||
|
||||
get(key: string, scope: StorageScope, fallbackValue: string): string;
|
||||
get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined;
|
||||
get(_key: string, _scope: StorageScope, fallbackValue?: string): string | undefined {
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean;
|
||||
getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined;
|
||||
getBoolean(_key: string, _scope: StorageScope, fallbackValue?: boolean): boolean | undefined {
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
getNumber(key: string, scope: StorageScope, fallbackValue: number): number;
|
||||
getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined;
|
||||
getNumber(_key: string, _scope: StorageScope, fallbackValue?: number): number | undefined {
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
getObject<T extends object>(key: string, scope: StorageScope, fallbackValue: T): T;
|
||||
getObject<T extends object>(key: string, scope: StorageScope, fallbackValue?: T): T | undefined;
|
||||
getObject<T extends object>(_key: string, _scope: StorageScope, fallbackValue?: T): T | undefined {
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
store(_key: string, _value: string | boolean | number | undefined | null, _scope: StorageScope, _target: StorageTarget): void {
|
||||
// no-op
|
||||
}
|
||||
|
||||
storeAll(_entries: IStorageEntry[], _external: boolean): void {
|
||||
// no-op
|
||||
}
|
||||
|
||||
remove(_key: string, _scope: StorageScope): void {
|
||||
// no-op
|
||||
}
|
||||
|
||||
isNew(_scope: StorageScope): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
flush(_reason?: WillSaveStateReason): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
optimize(_scope: StorageScope): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
log(): void {
|
||||
// no-op
|
||||
}
|
||||
|
||||
keys(_scope: StorageScope, _target: StorageTarget): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
switch(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
hasScope(_scope: IAnyWorkspaceIdentifier | IUserDataProfile): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Themes
|
||||
// ============================================================================
|
||||
|
||||
const themingRegistry = Registry.as<IThemingRegistry>(ThemingExtensions.ThemingContribution);
|
||||
const mockEnvironmentService: IEnvironmentService = Object.create(null);
|
||||
|
||||
export const darkTheme = ColorThemeData.createUnloadedThemeForThemeType(
|
||||
ColorScheme.DARK,
|
||||
COLOR_THEME_DARK_INITIAL_COLORS
|
||||
);
|
||||
|
||||
export const lightTheme = ColorThemeData.createUnloadedThemeForThemeType(
|
||||
ColorScheme.LIGHT,
|
||||
COLOR_THEME_LIGHT_INITIAL_COLORS
|
||||
);
|
||||
|
||||
let globalStyleSheet: CSSStyleSheet | undefined;
|
||||
let iconsStyleSheetCache: CSSStyleSheet | undefined;
|
||||
let darkThemeStyleSheet: CSSStyleSheet | undefined;
|
||||
let lightThemeStyleSheet: CSSStyleSheet | undefined;
|
||||
|
||||
function getGlobalStyleSheet(): CSSStyleSheet {
|
||||
if (!globalStyleSheet) {
|
||||
globalStyleSheet = new CSSStyleSheet();
|
||||
const globalRules: string[] = [];
|
||||
for (const sheet of Array.from(document.styleSheets)) {
|
||||
try {
|
||||
for (const rule of Array.from(sheet.cssRules)) {
|
||||
globalRules.push(rule.cssText);
|
||||
}
|
||||
} catch {
|
||||
// Cross-origin stylesheets can't be read
|
||||
}
|
||||
}
|
||||
globalStyleSheet.replaceSync(globalRules.join('\n'));
|
||||
}
|
||||
return globalStyleSheet;
|
||||
}
|
||||
|
||||
function getIconsStyleSheetCached(): CSSStyleSheet {
|
||||
if (!iconsStyleSheetCache) {
|
||||
iconsStyleSheetCache = new CSSStyleSheet();
|
||||
const iconsSheet = getIconsStyleSheet(undefined);
|
||||
iconsStyleSheetCache.replaceSync(iconsSheet.getCSS() as string);
|
||||
iconsSheet.dispose();
|
||||
}
|
||||
return iconsStyleSheetCache;
|
||||
}
|
||||
|
||||
function getThemeStyleSheet(theme: ColorThemeData): CSSStyleSheet {
|
||||
const isDark = theme.type === ColorScheme.DARK;
|
||||
if (isDark && darkThemeStyleSheet) {
|
||||
return darkThemeStyleSheet;
|
||||
}
|
||||
if (!isDark && lightThemeStyleSheet) {
|
||||
return lightThemeStyleSheet;
|
||||
}
|
||||
|
||||
const sheet = new CSSStyleSheet();
|
||||
const css = generateColorThemeCSS(
|
||||
theme,
|
||||
':host',
|
||||
themingRegistry.getThemingParticipants(),
|
||||
mockEnvironmentService
|
||||
);
|
||||
sheet.replaceSync(css.code);
|
||||
|
||||
if (isDark) {
|
||||
darkThemeStyleSheet = sheet;
|
||||
} else {
|
||||
lightThemeStyleSheet = sheet;
|
||||
}
|
||||
return sheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies theme styling to a shadow DOM container.
|
||||
* Adds theme class names and adopts shared stylesheets.
|
||||
*/
|
||||
export function setupTheme(container: HTMLElement, theme: ColorThemeData): void {
|
||||
container.classList.add(...theme.classNames);
|
||||
|
||||
const shadowRoot = container.getRootNode() as ShadowRoot;
|
||||
if (shadowRoot.adoptedStyleSheets !== undefined) {
|
||||
shadowRoot.adoptedStyleSheets = [
|
||||
getGlobalStyleSheet(),
|
||||
getIconsStyleSheetCached(),
|
||||
getThemeStyleSheet(theme),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Services
|
||||
// ============================================================================
|
||||
|
||||
export interface ServiceRegistration {
|
||||
define<T>(id: ServiceIdentifier<T>, ctor: new (...args: never[]) => T): void;
|
||||
defineInstance<T>(id: ServiceIdentifier<T>, instance: T): void;
|
||||
}
|
||||
|
||||
export interface CreateServicesOptions {
|
||||
/**
|
||||
* The color theme to use for the theme service.
|
||||
*/
|
||||
colorTheme?: IColorTheme;
|
||||
/**
|
||||
* Additional services to register after the base editor services.
|
||||
*/
|
||||
additionalServices?: (registration: ServiceRegistration) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a TestInstantiationService with all services needed for CodeEditorWidget.
|
||||
* Additional services can be registered via the options callback.
|
||||
*/
|
||||
export function createEditorServices(disposables: DisposableStore, options?: CreateServicesOptions): TestInstantiationService {
|
||||
const services = new ServiceCollection();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const serviceIdentifiers: ServiceIdentifier<any>[] = [];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const define = <T>(id: ServiceIdentifier<T>, ctor: new (...args: any[]) => T) => {
|
||||
if (!services.has(id)) {
|
||||
services.set(id, new SyncDescriptor(ctor));
|
||||
}
|
||||
serviceIdentifiers.push(id);
|
||||
};
|
||||
|
||||
const defineInstance = <T>(id: ServiceIdentifier<T>, instance: T) => {
|
||||
if (!services.has(id)) {
|
||||
services.set(id, instance);
|
||||
}
|
||||
serviceIdentifiers.push(id);
|
||||
};
|
||||
|
||||
// Base editor services
|
||||
define(IAccessibilityService, TestAccessibilityService);
|
||||
define(IKeybindingService, MockKeybindingService);
|
||||
define(IClipboardService, TestClipboardService);
|
||||
define(IEditorWorkerService, TestEditorWorkerService);
|
||||
defineInstance(IOpenerService, NullOpenerService);
|
||||
define(INotificationService, TestNotificationService);
|
||||
define(IDialogService, TestDialogService);
|
||||
define(IUndoRedoService, UndoRedoService);
|
||||
define(ILanguageService, LanguageService);
|
||||
define(ILanguageConfigurationService, TestLanguageConfigurationService);
|
||||
define(IConfigurationService, TestConfigurationService);
|
||||
define(ITextResourcePropertiesService, TestTextResourcePropertiesService);
|
||||
defineInstance(IStorageService, new NullStorageService());
|
||||
if (options?.colorTheme) {
|
||||
defineInstance(IThemeService, new TestThemeService(options.colorTheme));
|
||||
} else {
|
||||
define(IThemeService, TestThemeService);
|
||||
}
|
||||
define(ILogService, NullLogService);
|
||||
define(IModelService, ModelService);
|
||||
define(ICodeEditorService, TestCodeEditorService);
|
||||
define(IContextKeyService, MockContextKeyService);
|
||||
define(ICommandService, TestCommandService);
|
||||
define(ITelemetryService, NullTelemetryServiceShape);
|
||||
define(ILoggerService, NullLoggerService);
|
||||
define(IDataChannelService, NullDataChannelService);
|
||||
define(IEnvironmentService, class extends mock<IEnvironmentService>() {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
override isBuilt: boolean = true;
|
||||
override isExtensionDevelopment: boolean = false;
|
||||
});
|
||||
define(ILanguageFeatureDebounceService, LanguageFeatureDebounceService);
|
||||
define(ILanguageFeaturesService, LanguageFeaturesService);
|
||||
define(ITreeSitterLibraryService, TestTreeSitterLibraryService);
|
||||
define(IInlineCompletionsService, InlineCompletionsService);
|
||||
defineInstance(ICodeLensCache, {
|
||||
_serviceBrand: undefined,
|
||||
put: () => { },
|
||||
get: () => undefined,
|
||||
delete: () => { },
|
||||
} as ICodeLensCache);
|
||||
defineInstance(IHoverService, {
|
||||
_serviceBrand: undefined,
|
||||
showDelayedHover: () => undefined,
|
||||
setupDelayedHover: () => ({ dispose: () => { } }),
|
||||
setupDelayedHoverAtMouse: () => ({ dispose: () => { } }),
|
||||
showInstantHover: () => undefined,
|
||||
hideHover: () => { },
|
||||
showAndFocusLastHover: () => { },
|
||||
setupManagedHover: () => ({ dispose: () => { }, show: () => { }, hide: () => { }, update: () => { } }),
|
||||
showManagedHover: () => { },
|
||||
} as IHoverService);
|
||||
defineInstance(IDefaultAccountService, {
|
||||
_serviceBrand: undefined,
|
||||
onDidChangeDefaultAccount: new Emitter<null>().event,
|
||||
onDidChangePolicyData: new Emitter<null>().event,
|
||||
policyData: null,
|
||||
getDefaultAccount: async () => null,
|
||||
getDefaultAccountAuthenticationProvider: () => ({ id: 'test', name: 'Test', scopes: [], enterprise: false }),
|
||||
setDefaultAccountProvider: () => { },
|
||||
refresh: async () => null,
|
||||
signIn: async () => null,
|
||||
} as IDefaultAccountService);
|
||||
|
||||
// User interaction service with focus simulation enabled (all elements appear focused in fixtures)
|
||||
defineInstance(IUserInteractionService, new MockUserInteractionService(true, false));
|
||||
|
||||
// Allow additional services to be registered
|
||||
options?.additionalServices?.({ define, defineInstance });
|
||||
|
||||
const instantiationService = disposables.add(new TestInstantiationService(services, true));
|
||||
|
||||
disposables.add(toDisposable(() => {
|
||||
for (const id of serviceIdentifiers) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const instanceOrDescriptor = services.get(id) as any;
|
||||
if (typeof instanceOrDescriptor?.dispose === 'function') {
|
||||
instanceOrDescriptor.dispose();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
return instantiationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers additional services needed by workbench components (merge editor, etc.).
|
||||
* Use with createEditorServices additionalServices option.
|
||||
*/
|
||||
export function registerWorkbenchServices(registration: ServiceRegistration): void {
|
||||
registration.defineInstance(IContextMenuService, {
|
||||
showContextMenu: () => { },
|
||||
onDidShowContextMenu: () => ({ dispose: () => { } }),
|
||||
onDidHideContextMenu: () => ({ dispose: () => { } }),
|
||||
} as unknown as IContextMenuService);
|
||||
|
||||
registration.defineInstance(IContextViewService, {
|
||||
showContextView: () => ({ dispose: () => { } }),
|
||||
hideContextView: () => { },
|
||||
getContextViewElement: () => null,
|
||||
layout: () => { },
|
||||
} as unknown as IContextViewService);
|
||||
|
||||
registration.defineInstance(ILabelService, {
|
||||
getUriLabel: (uri: URI) => uri.path,
|
||||
getUriBasenameLabel: (uri: URI) => uri.path.split('/').pop() ?? '',
|
||||
getWorkspaceLabel: () => '',
|
||||
getHostLabel: () => '',
|
||||
getSeparator: () => '/',
|
||||
registerFormatter: () => ({ dispose: () => { } }),
|
||||
onDidChangeFormatters: () => ({ dispose: () => { } }),
|
||||
registerCachedFormatter: () => ({ dispose: () => { } }),
|
||||
} as unknown as ILabelService);
|
||||
|
||||
registration.define(IMenuService, TestMenuService);
|
||||
registration.define(IActionViewItemService, NullActionViewItemService);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Text Models
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Creates a text model using the ModelService.
|
||||
*/
|
||||
export function createTextModel(
|
||||
instantiationService: TestInstantiationService,
|
||||
text: string,
|
||||
uri: URI,
|
||||
languageId?: string
|
||||
): ITextModel {
|
||||
const modelService = instantiationService.get(IModelService);
|
||||
const languageService = instantiationService.get(ILanguageService);
|
||||
const languageSelection = languageId ? languageService.createById(languageId) : null;
|
||||
return modelService.createModel(text, languageSelection, uri);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Fixture Adapters
|
||||
// ============================================================================
|
||||
|
||||
export interface ComponentFixtureContext {
|
||||
container: HTMLElement;
|
||||
disposableStore: DisposableStore;
|
||||
theme: ColorThemeData;
|
||||
}
|
||||
|
||||
export interface ComponentFixtureOptions {
|
||||
render: (context: ComponentFixtureContext) => HTMLElement | Promise<HTMLElement>;
|
||||
}
|
||||
|
||||
type ThemedFixtures = ReturnType<typeof defineFixtureVariants>;
|
||||
|
||||
/**
|
||||
* Creates Dark and Light fixture variants from a single render function.
|
||||
* The render function receives a context with container and disposableStore.
|
||||
*/
|
||||
export function defineComponentFixture(options: ComponentFixtureOptions): ThemedFixtures {
|
||||
const createFixture = (theme: typeof darkTheme | typeof lightTheme) => defineFixture({
|
||||
isolation: 'shadow-dom',
|
||||
displayMode: { type: 'component' },
|
||||
properties: [],
|
||||
background: theme === darkTheme ? 'dark' : 'light',
|
||||
render: async (container: HTMLElement) => {
|
||||
const disposableStore = new DisposableStore();
|
||||
setupTheme(container, theme);
|
||||
return options.render({ container, disposableStore, theme });
|
||||
},
|
||||
});
|
||||
|
||||
return defineFixtureVariants({
|
||||
Dark: createFixture(darkTheme),
|
||||
Light: createFixture(lightTheme),
|
||||
});
|
||||
}
|
||||
|
||||
type ThemedFixtureGroupInput = Record<string, ThemedFixtures>;
|
||||
|
||||
/**
|
||||
* Creates a nested fixture group from themed fixtures.
|
||||
* E.g., { MergeEditor: { Dark: ..., Light: ... } } becomes a nested group: MergeEditor > Dark/Light
|
||||
*/
|
||||
export function defineThemedFixtureGroup(group: ThemedFixtureGroupInput): ReturnType<typeof defineFixtureGroup> {
|
||||
return defineFixtureGroup(group);
|
||||
}
|
||||
1223
build/vite/package-lock.json
generated
1223
build/vite/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,14 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vscode/rollup-plugin-esm-url": "^1.0.1-0",
|
||||
"vite": "^7.1.11"
|
||||
"@vscode/rollup-plugin-esm-url": "^1.0.1-1",
|
||||
"vite": "npm:rolldown-vite@latest",
|
||||
"@vscode/component-explorer": "0.1.1-1",
|
||||
"@vscode/component-explorer-vite-plugin": "0.1.1-1"
|
||||
},
|
||||
"overrides": {
|
||||
"@vscode/component-explorer-vite-plugin": {
|
||||
"vite": "$vite"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { getSingletonServiceDescriptors, InstantiationType, registerSingleton }
|
||||
import { IWebWorkerService } from '../../src/vs/platform/webWorker/browser/webWorkerService.ts';
|
||||
// eslint-disable-next-line local/code-no-standalone-editor
|
||||
import { StandaloneWebWorkerService } from '../../src/vs/editor/standalone/browser/services/standaloneWebWorkerService.ts';
|
||||
import './style.css';
|
||||
|
||||
enableHotReload();
|
||||
|
||||
|
||||
@@ -7,3 +7,9 @@
|
||||
height: 400px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "codicon";
|
||||
font-display: block;
|
||||
src: url("~@vscode/codicons/dist/codicon.ttf") format("truetype");
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
|
||||
import { createLogger, defineConfig, Plugin } from 'vite';
|
||||
import path, { join } from 'path';
|
||||
import { rollupEsmUrlPlugin } from '@vscode/rollup-plugin-esm-url';
|
||||
// import { componentExplorer } from 'D:/dev/hediet/js-component-explorer/packages/vite-plugin';
|
||||
import { componentExplorer } from '@vscode/component-explorer-vite-plugin';
|
||||
import { statSync } from 'fs';
|
||||
import { pathToFileURL } from 'url';
|
||||
import { rollupEsmUrlPlugin } from '@vscode/rollup-plugin-esm-url';
|
||||
|
||||
function injectBuiltinExtensionsPlugin(): Plugin {
|
||||
let builtinExtensionsCache: unknown[] | null = null;
|
||||
@@ -166,9 +168,20 @@ export default defineConfig({
|
||||
plugins: [
|
||||
rollupEsmUrlPlugin({}),
|
||||
injectBuiltinExtensionsPlugin(),
|
||||
createHotClassSupport()
|
||||
createHotClassSupport(),
|
||||
componentExplorer({
|
||||
logLevel: 'verbose',
|
||||
include: 'build/vite/**/*.fixture.ts',
|
||||
}),
|
||||
],
|
||||
customLogger: logger,
|
||||
resolve: {
|
||||
alias: {
|
||||
// '@vscode/component-explorer': 'D:/dev/hediet/js-component-explorer/packages/explorer/src',
|
||||
'@vscode/component-explorer': path.resolve(__dirname, 'node_modules/@vscode/component-explorer'),
|
||||
'~@vscode/codicons': '/node_modules/@vscode/codicons',
|
||||
}
|
||||
},
|
||||
esbuild: {
|
||||
tsconfigRaw: {
|
||||
compilerOptions: {
|
||||
|
||||
Reference in New Issue
Block a user