mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-02 00:09:30 +01:00
updates component explorer
This commit is contained in:
committed by
Henning Dieterichs
parent
0f688387c1
commit
5c4204e60d
2
.github/workflows/screenshot-test.yml
vendored
2
.github/workflows/screenshot-test.yml
vendored
@@ -24,6 +24,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
||||
9
.vscode/launch.json
vendored
9
.vscode/launch.json
vendored
@@ -604,12 +604,13 @@
|
||||
},
|
||||
{
|
||||
"name": "Component Explorer",
|
||||
"type": "chrome",
|
||||
"type": "msedge",
|
||||
"port": 9230,
|
||||
"request": "launch",
|
||||
"url": "http://localhost:5199/___explorer",
|
||||
"preLaunchTask": "Launch Monaco Editor Vite",
|
||||
"url": "http://localhost:5337/___explorer",
|
||||
"preLaunchTask": "Launch Component Explorer",
|
||||
"presentation": {
|
||||
"group": "monaco",
|
||||
"group": "1_component_explorer",
|
||||
"order": 4
|
||||
}
|
||||
},
|
||||
|
||||
20
.vscode/mcp.json
vendored
20
.vscode/mcp.json
vendored
@@ -3,9 +3,25 @@
|
||||
"vscode-automation-mcp": {
|
||||
"type": "stdio",
|
||||
"command": "npm",
|
||||
// Look at the [README](../test/mcp/README.md) to see what arguments are supported
|
||||
"args": ["run", "start-stdio"],
|
||||
"args": [
|
||||
"run",
|
||||
"start-stdio"
|
||||
],
|
||||
"cwd": "${workspaceFolder}/test/mcp"
|
||||
},
|
||||
"component-explorer": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"args": [
|
||||
"component-explorer",
|
||||
"mcp",
|
||||
"-c",
|
||||
"./test/componentFixtures/component-explorer.json",
|
||||
"--no-daemon-autostart",
|
||||
"--no-daemon-hint",
|
||||
"Start the daemon by running the 'Launch Component Explorer' VS Code task (use the run_task tool). When you start the task, try up to 4 times to give the daemon enough time to start."
|
||||
]
|
||||
}
|
||||
},
|
||||
"inputs": []
|
||||
|
||||
7
.vscode/tasks.json
vendored
7
.vscode/tasks.json
vendored
@@ -357,6 +357,13 @@
|
||||
"problemMatcher": [
|
||||
"$tsc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Launch Component Explorer",
|
||||
"type": "shell",
|
||||
"command": "npx component-explorer serve -c ./test/componentFixtures/component-explorer.json",
|
||||
"isBackground": true,
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ interface RenderAiStatsOptions extends ComponentFixtureContext {
|
||||
data: IAiStatsHoverData;
|
||||
}
|
||||
|
||||
function renderAiStatsHover({ container, disposableStore, data }: RenderAiStatsOptions): HTMLElement {
|
||||
function renderAiStatsHover({ container, disposableStore, data }: RenderAiStatsOptions): void {
|
||||
container.style.width = '320px';
|
||||
container.style.padding = '8px';
|
||||
container.style.backgroundColor = 'var(--vscode-editorHoverWidget-background)';
|
||||
@@ -87,6 +87,4 @@ function renderAiStatsHover({ container, disposableStore, data }: RenderAiStatsO
|
||||
|
||||
const elem = hover.keepUpdated(disposableStore).element;
|
||||
container.appendChild(elem);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ const themedProgressBarOptions = {
|
||||
// Buttons
|
||||
// ============================================================================
|
||||
|
||||
function renderButtons({ container, disposableStore }: ComponentFixtureContext): HTMLElement {
|
||||
function renderButtons({ container, disposableStore }: ComponentFixtureContext): void {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
@@ -162,11 +162,9 @@ function renderButtons({ container, disposableStore }: ComponentFixtureContext):
|
||||
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 {
|
||||
function renderButtonBar({ container, disposableStore }: ComponentFixtureContext): void {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
@@ -193,8 +191,6 @@ function renderButtonBar({ container, disposableStore }: ComponentFixtureContext
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -202,7 +198,7 @@ function renderButtonBar({ container, disposableStore }: ComponentFixtureContext
|
||||
// Toggles and Checkboxes
|
||||
// ============================================================================
|
||||
|
||||
function renderToggles({ container, disposableStore }: ComponentFixtureContext): HTMLElement {
|
||||
function renderToggles({ container, disposableStore }: ComponentFixtureContext): void {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
@@ -266,8 +262,6 @@ function renderToggles({ container, disposableStore }: ComponentFixtureContext):
|
||||
checkboxSection.appendChild(createCheckboxRow('Enable auto-save', true));
|
||||
checkboxSection.appendChild(createCheckboxRow('Show line numbers', true));
|
||||
checkboxSection.appendChild(createCheckboxRow('Word wrap', false));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
@@ -275,7 +269,7 @@ function renderToggles({ container, disposableStore }: ComponentFixtureContext):
|
||||
// Input Boxes
|
||||
// ============================================================================
|
||||
|
||||
function renderInputBoxes({ container, disposableStore }: ComponentFixtureContext): HTMLElement {
|
||||
function renderInputBoxes({ container, disposableStore }: ComponentFixtureContext): void {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
@@ -321,8 +315,6 @@ function renderInputBoxes({ container, disposableStore }: ComponentFixtureContex
|
||||
}));
|
||||
errorInput.value = 'invalid-email';
|
||||
errorInput.validate();
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
@@ -330,7 +322,7 @@ function renderInputBoxes({ container, disposableStore }: ComponentFixtureContex
|
||||
// Count Badges
|
||||
// ============================================================================
|
||||
|
||||
function renderCountBadges({ container }: ComponentFixtureContext): HTMLElement {
|
||||
function renderCountBadges({ container }: ComponentFixtureContext): void {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.gap = '12px';
|
||||
@@ -353,8 +345,6 @@ function renderCountBadges({ container }: ComponentFixtureContext): HTMLElement
|
||||
new CountBadge(badgeContainer, { count }, themedBadgeStyles);
|
||||
container.appendChild(badgeContainer);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
@@ -362,7 +352,7 @@ function renderCountBadges({ container }: ComponentFixtureContext): HTMLElement
|
||||
// Action Bar
|
||||
// ============================================================================
|
||||
|
||||
function renderActionBar({ container, disposableStore }: ComponentFixtureContext): HTMLElement {
|
||||
function renderActionBar({ container, disposableStore }: ComponentFixtureContext): void {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
@@ -410,8 +400,6 @@ function renderActionBar({ container, disposableStore }: ComponentFixtureContext
|
||||
new Action('action.disabled', 'Disabled', ThemeIcon.asClassName(Codicon.debugPause), false, async () => { }),
|
||||
new Action('action.enabled2', 'Enabled', ThemeIcon.asClassName(Codicon.debugStop), true, async () => { }),
|
||||
]);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
@@ -419,7 +407,7 @@ function renderActionBar({ container, disposableStore }: ComponentFixtureContext
|
||||
// Progress Bar
|
||||
// ============================================================================
|
||||
|
||||
function renderProgressBars({ container, disposableStore }: ComponentFixtureContext): HTMLElement {
|
||||
function renderProgressBars({ container, disposableStore }: ComponentFixtureContext): void {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
@@ -470,8 +458,6 @@ function renderProgressBars({ container, disposableStore }: ComponentFixtureCont
|
||||
const doneBar = disposableStore.add(new ProgressBar(doneSection, themedProgressBarOptions));
|
||||
doneBar.total(100);
|
||||
doneBar.worked(100);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
@@ -479,7 +465,7 @@ function renderProgressBars({ container, disposableStore }: ComponentFixtureCont
|
||||
// Highlighted Label
|
||||
// ============================================================================
|
||||
|
||||
function renderHighlightedLabels({ container }: ComponentFixtureContext): HTMLElement {
|
||||
function renderHighlightedLabels({ container }: ComponentFixtureContext): void {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
@@ -511,6 +497,4 @@ function renderHighlightedLabels({ container }: ComponentFixtureContext): HTMLEl
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ console.log(greet('World'));
|
||||
console.log(\`Count: \${counter.count}\`);
|
||||
`;
|
||||
|
||||
function renderCodeEditor({ container, disposableStore, theme }: ComponentFixtureContext): HTMLElement {
|
||||
function renderCodeEditor({ container, disposableStore, theme }: ComponentFixtureContext): void {
|
||||
container.style.width = '600px';
|
||||
container.style.height = '400px';
|
||||
container.style.border = '1px solid var(--vscode-editorWidget-border)';
|
||||
@@ -66,8 +66,6 @@ function renderCodeEditor({ container, disposableStore, theme }: ComponentFixtur
|
||||
));
|
||||
|
||||
editor.setModel(model);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
export default defineThemedFixtureGroup({
|
||||
|
||||
@@ -95,6 +95,7 @@ import '../../../../platform/theme/common/colors/editorColors.js';
|
||||
import '../../../../platform/theme/common/colors/listColors.js';
|
||||
import '../../../../platform/theme/common/colors/miscColors.js';
|
||||
import '../../../common/theme.js';
|
||||
import { isThenable } from '../../../../base/common/async.js';
|
||||
|
||||
/**
|
||||
* A storage service that never stores anything and always returns the default/fallback value.
|
||||
@@ -478,7 +479,7 @@ export interface ComponentFixtureContext {
|
||||
}
|
||||
|
||||
export interface ComponentFixtureOptions {
|
||||
render: (context: ComponentFixtureContext) => HTMLElement | Promise<HTMLElement>;
|
||||
render: (context: ComponentFixtureContext) => void | Promise<void>;
|
||||
}
|
||||
|
||||
type ThemedFixtures = ReturnType<typeof defineFixtureVariants>;
|
||||
@@ -486,6 +487,10 @@ 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.
|
||||
*
|
||||
* Note: If render returns a Promise, the async work will run in background.
|
||||
* Component-explorer waits 2 animation frames after sync render returns,
|
||||
* which should be sufficient for most async setup, but timing is not guaranteed.
|
||||
*/
|
||||
export function defineComponentFixture(options: ComponentFixtureOptions): ThemedFixtures {
|
||||
const createFixture = (theme: typeof darkTheme | typeof lightTheme) => defineFixture({
|
||||
@@ -496,8 +501,9 @@ export function defineComponentFixture(options: ComponentFixtureOptions): Themed
|
||||
render: (container: HTMLElement) => {
|
||||
const disposableStore = new DisposableStore();
|
||||
setupTheme(container, theme);
|
||||
options.render({ container, disposableStore, theme });
|
||||
return disposableStore;
|
||||
// Start render (may be async) - component-explorer will wait 2 rAF after this returns
|
||||
const result = options.render({ container, disposableStore, theme });
|
||||
return isThenable(result) ? result.then(() => disposableStore) : disposableStore;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ interface InlineEditOptions extends ComponentFixtureContext {
|
||||
editorOptions?: IEditorOptions;
|
||||
}
|
||||
|
||||
function renderInlineEdit(options: InlineEditOptions): HTMLElement {
|
||||
function renderInlineEdit(options: InlineEditOptions): void {
|
||||
const { container, disposableStore, theme } = options;
|
||||
container.style.width = options.width ?? '500px';
|
||||
container.style.height = options.height ?? '170px';
|
||||
@@ -100,8 +100,6 @@ function renderInlineEdit(options: InlineEditOptions): HTMLElement {
|
||||
// Trigger inline completions
|
||||
const controller = InlineCompletionsController.get(editor);
|
||||
controller?.model?.get();
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { mainWindow } from '../../../../base/browser/window.js';
|
||||
import { CancellationToken } from '../../../../base/common/cancellation.js';
|
||||
import { Event } from '../../../../base/common/event.js';
|
||||
import { ResourceSet } from '../../../../base/common/map.js';
|
||||
@@ -85,15 +86,7 @@ export default defineThemedFixtureGroup({
|
||||
}),
|
||||
});
|
||||
|
||||
async function renderPromptFilePickerFixture({ container, disposableStore, theme, type, placeholder, seedData }: RenderPromptPickerOptions): Promise<HTMLElement> {
|
||||
container.style.width = 'fit-content';
|
||||
container.style.minHeight = '0';
|
||||
container.style.padding = '8px';
|
||||
container.style.boxSizing = 'border-box';
|
||||
container.style.background = 'var(--vscode-editor-background)';
|
||||
container.style.border = '1px solid var(--vscode-editorWidget-border)';
|
||||
container.style.position = 'relative';
|
||||
|
||||
async function renderPromptFilePickerFixture({ container, disposableStore, theme, type, placeholder, seedData }: RenderPromptPickerOptions): Promise<void> {
|
||||
const quickInputHost = document.createElement('div');
|
||||
quickInputHost.style.position = 'relative';
|
||||
const hostWidth = 800;
|
||||
@@ -207,5 +200,45 @@ async function renderPromptFilePickerFixture({ container, disposableStore, theme
|
||||
type,
|
||||
});
|
||||
|
||||
return container;
|
||||
// Wait for the quickpick widget to render and have dimensions
|
||||
const quickInputWidget = await waitForElement<HTMLElement>(
|
||||
quickInputHost,
|
||||
'.quick-input-widget',
|
||||
el => el.offsetWidth > 0 && el.offsetHeight > 0
|
||||
);
|
||||
|
||||
if (quickInputWidget) {
|
||||
// Reset positioning
|
||||
quickInputWidget.style.position = 'relative';
|
||||
quickInputWidget.style.top = '0';
|
||||
quickInputWidget.style.left = '0';
|
||||
|
||||
// Move widget to container and remove host
|
||||
container.appendChild(quickInputWidget);
|
||||
quickInputHost.remove();
|
||||
|
||||
// Set explicit dimensions on container to match widget
|
||||
const rect = quickInputWidget.getBoundingClientRect();
|
||||
container.style.width = `${rect.width}px`;
|
||||
container.style.height = `${rect.height}px`;
|
||||
}
|
||||
}
|
||||
|
||||
async function waitForElement<T extends HTMLElement>(
|
||||
root: HTMLElement,
|
||||
selector: string,
|
||||
condition: (el: T) => boolean,
|
||||
timeout = 2000
|
||||
): Promise<T | null> {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < timeout) {
|
||||
const el = root.querySelector<T>(selector);
|
||||
if (el && condition(el)) {
|
||||
// Wait one more frame to ensure layout is complete
|
||||
await new Promise(resolve => mainWindow.requestAnimationFrame(resolve));
|
||||
return el;
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
}
|
||||
return root.querySelector<T>(selector);
|
||||
}
|
||||
|
||||
183
test/componentFixtures/component-explorer-config.schema.json
Normal file
183
test/componentFixtures/component-explorer-config.schema.json
Normal file
@@ -0,0 +1,183 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"screenshotDir": {
|
||||
"description": "Directory for storing screenshots (default: .screenshots)",
|
||||
"type": "string"
|
||||
},
|
||||
"sessions": {
|
||||
"minItems": 1,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Unique session name"
|
||||
},
|
||||
"source": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"const": "current"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"worktree": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ref": {
|
||||
"type": "string",
|
||||
"description": "Git ref (branch, tag, or commit) to check out"
|
||||
},
|
||||
"name": {
|
||||
"description": "Directory name for the worktree (default: component-explorer-baseline)",
|
||||
"type": "string"
|
||||
},
|
||||
"install": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"const": "auto"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"const": "npm"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"const": "pnpm"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"const": "yarn"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"command": {
|
||||
"type": "string",
|
||||
"description": "Custom install command to run in the worktree"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"command"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"const": false
|
||||
}
|
||||
],
|
||||
"description": "Dependency install strategy for the worktree"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"ref"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"description": "Git worktree configuration for a baseline session"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"worktree"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
],
|
||||
"description": "Session source: \"current\" for the working tree, or a worktree config for a baseline"
|
||||
},
|
||||
"viteConfig": {
|
||||
"description": "Path to vite config file, relative to this config (overrides top-level viteConfig)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"description": "A component explorer session"
|
||||
},
|
||||
"description": "List of explorer sessions"
|
||||
},
|
||||
"compare": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"baseline": {
|
||||
"type": "string",
|
||||
"description": "Session name to use as the baseline for comparisons"
|
||||
},
|
||||
"current": {
|
||||
"type": "string",
|
||||
"description": "Session name to use as the current version for comparisons"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"baseline",
|
||||
"current"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"description": "Screenshot comparison configuration"
|
||||
},
|
||||
"viteConfig": {
|
||||
"description": "Default vite config file path, relative to this config (default: vite.config.ts)",
|
||||
"type": "string"
|
||||
},
|
||||
"vite": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"hmr": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allowedPaths": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Glob patterns for files that keep HMR; everything else triggers a full reload"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"allowedPaths"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"description": "Vite HMR configuration"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"description": "Vite configuration overrides"
|
||||
},
|
||||
"redirection": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 65535,
|
||||
"description": "Port for the redirection HTTP server"
|
||||
},
|
||||
"host": {
|
||||
"description": "Host to bind the redirection server to (default: localhost)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"port"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"description": "HTTP redirection server that redirects to the session URL"
|
||||
},
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"description": "URL of the JSON Schema for this config file"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sessions"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"description": "Component Explorer configuration"
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
{
|
||||
"$schema": "./component-explorer-config.schema.json",
|
||||
"screenshotDir": ".screenshots",
|
||||
"sessions": [
|
||||
{
|
||||
"name": "current"
|
||||
}
|
||||
],
|
||||
"redirection": {
|
||||
"port": 5337
|
||||
},
|
||||
"viteConfig": "../../build/vite/vite.config.ts",
|
||||
"vite": {
|
||||
"hmr": {
|
||||
|
||||
Reference in New Issue
Block a user