From d2efa707ff68b2d384c3de4e98a1bf62dd244f55 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Tue, 21 Jul 2026 21:24:06 +0300 Subject: [PATCH] fix(vscode): detect workspace before runtime registration --- packages/ui/src/stores/useProjectsStore.ts | 18 ++++-------------- .../ui/src/stores/utils/vscodeRuntime.test.ts | 15 +++++++++++++++ packages/ui/src/stores/utils/vscodeRuntime.ts | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+), 14 deletions(-) create mode 100644 packages/ui/src/stores/utils/vscodeRuntime.test.ts create mode 100644 packages/ui/src/stores/utils/vscodeRuntime.ts diff --git a/packages/ui/src/stores/useProjectsStore.ts b/packages/ui/src/stores/useProjectsStore.ts index d4c93952..fbf279b3 100644 --- a/packages/ui/src/stores/useProjectsStore.ts +++ b/packages/ui/src/stores/useProjectsStore.ts @@ -13,6 +13,7 @@ import { PROJECT_COLORS } from '@/lib/projectMeta'; import { useSessionUIStore } from '@/sync/session-ui-store'; import { runtimeFetch } from '@/lib/runtime-fetch'; import { getRuntimeApiBaseUrl } from '@/lib/runtime-switch'; +import { getVSCodeBootstrapConfig, isVSCodeRuntime } from './utils/vscodeRuntime'; /** Pick a color key that's least used among existing projects */ const pickAutoColor = (projects: ProjectEntry[]): string => { @@ -416,21 +417,11 @@ const createVSCodeWorkspaceProject = ( }; const getVSCodeWorkspaceFolders = (): VSCodeWorkspaceFolderConfig[] | null => { - if (typeof window === 'undefined') { - return null; - } - const runtimeApis = getRegisteredRuntimeAPIs(); - if (!runtimeApis?.runtime?.isVSCode) { + const config = getVSCodeBootstrapConfig(); + if (!isVSCodeRuntime(runtimeApis, config)) { return null; } - - const config = (window as unknown as { - __VSCODE_CONFIG__?: { - workspaceFolder?: unknown; - workspaceFolders?: unknown; - }; - }).__VSCODE_CONFIG__; const folders = Array.isArray(config?.workspaceFolders) ? config.workspaceFolders .map((entry) => { @@ -542,8 +533,7 @@ const getVSCodeWorkspaceProject = (): { projects: ProjectEntry[]; activeProjectI // Always prefer the VS Code workspace projects over any persisted multi-project registry. const vscodeWorkspace = getVSCodeWorkspaceProject(); const isVSCodeProjectsRuntime = (() => { - if (typeof window === 'undefined') return false; - return Boolean(getRegisteredRuntimeAPIs()?.runtime?.isVSCode); + return isVSCodeRuntime(getRegisteredRuntimeAPIs(), getVSCodeBootstrapConfig()); })(); const effectiveInitialProjects = vscodeWorkspace?.projects ?? (isVSCodeProjectsRuntime ? [] : initialProjects); const persistedInitialActiveProjectId = vscodeWorkspace?.activeProjectId ?? (isVSCodeProjectsRuntime ? null : readPersistedActiveProjectId()); diff --git a/packages/ui/src/stores/utils/vscodeRuntime.test.ts b/packages/ui/src/stores/utils/vscodeRuntime.test.ts new file mode 100644 index 00000000..aebe538c --- /dev/null +++ b/packages/ui/src/stores/utils/vscodeRuntime.test.ts @@ -0,0 +1,15 @@ +import { describe, expect, test } from 'bun:test'; +import { isVSCodeRuntime } from './vscodeRuntime'; + +describe('VS Code runtime detection', () => { + test('uses extension-host bootstrap config before runtime APIs are registered', () => { + expect(isVSCodeRuntime(null, { + workspaceFolder: '/workspace/project-one', + workspaceFolders: [{ name: 'project-one', path: '/workspace/project-one' }], + })).toBe(true); + }); + + test('does not classify an unregistered web runtime as VS Code', () => { + expect(isVSCodeRuntime(null, null)).toBe(false); + }); +}); diff --git a/packages/ui/src/stores/utils/vscodeRuntime.ts b/packages/ui/src/stores/utils/vscodeRuntime.ts new file mode 100644 index 00000000..91446ce6 --- /dev/null +++ b/packages/ui/src/stores/utils/vscodeRuntime.ts @@ -0,0 +1,18 @@ +import type { RuntimeAPIs } from '@/lib/api/types'; + +export interface VSCodeBootstrapConfig { + workspaceFolder?: unknown; + workspaceFolders?: unknown; +} + +export const getVSCodeBootstrapConfig = (): VSCodeBootstrapConfig | null => { + if (typeof window === 'undefined') { + return null; + } + return (window as unknown as { __VSCODE_CONFIG__?: VSCodeBootstrapConfig }).__VSCODE_CONFIG__ ?? null; +}; + +export const isVSCodeRuntime = ( + runtimeApis: RuntimeAPIs | null, + bootstrapConfig = getVSCodeBootstrapConfig(), +): boolean => Boolean(bootstrapConfig || runtimeApis?.runtime?.isVSCode);