fix Windows folder import from project modal (#2863)

Co-authored-by: Lanzhou3 <217479610+Lanzhou3@users.noreply.github.com>
This commit is contained in:
蓝宙 2026-05-26 14:52:07 +08:00 committed by GitHub
parent 1770789fa0
commit b5b975769a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 58 additions and 2 deletions

View file

@ -107,8 +107,11 @@ function normalizeProjectImportResult(input: unknown): OpenDesignHostProjectImpo
const rawProjectId = isRecord(project) ? project.id : null;
const projectId = typeof rawProjectId === 'string' ? rawProjectId : null;
const conversationId = typeof response.conversationId === 'string' ? response.conversationId : null;
const entryFile = typeof response.entryFile === 'string' ? response.entryFile : null;
if (projectId == null || conversationId == null || entryFile == null) {
const entryFile =
typeof response.entryFile === 'string' || response.entryFile === null
? response.entryFile
: undefined;
if (projectId == null || conversationId == null || entryFile === undefined) {
return failure('daemon import response did not include host project identifiers', response);
}

View file

@ -33,4 +33,12 @@ describe("desktop preload host boundary", () => {
expect(source).not.toContain('exposeInMainWorld("__odDesktop"');
expect(source).not.toContain("exposeInMainWorld('__odDesktop'");
});
it("mirrors the host import contract by accepting a null entryFile", () => {
const here = dirname(fileURLToPath(import.meta.url));
const source = readFileSync(join(here, "../../src/main/preload.cts"), "utf8");
expect(source).toContain("response.entryFile === null");
expect(source).toContain("entryFile === undefined");
});
});

View file

@ -10,6 +10,7 @@
import { useEffect, useRef, useState } from 'react';
import type { ConnectorDetail } from '@open-design/contracts';
import type { OpenDesignHostProjectImportSuccess } from '@open-design/host';
import type {
DesignSystemSummary,
MediaProviderCredentials,
@ -35,6 +36,7 @@ interface Props {
onCreate: (input: CreateInput & { requestId?: string }) => Promise<boolean> | boolean | void;
onImportClaudeDesign?: (file: File) => Promise<void> | void;
onImportFolder?: (baseDir: string) => Promise<void> | void;
onImportFolderResponse?: (response: OpenDesignHostProjectImportSuccess) => Promise<void> | void;
onOpenConnectorsTab?: () => void;
onClose: () => void;
initialTab?: CreateTab;
@ -55,6 +57,7 @@ export function NewProjectModal({
onCreate,
onImportClaudeDesign,
onImportFolder,
onImportFolderResponse,
onOpenConnectorsTab,
onClose,
initialTab,
@ -151,6 +154,7 @@ export function NewProjectModal({
}}
{...(onImportClaudeDesign ? { onImportClaudeDesign } : {})}
{...(onImportFolder ? { onImportFolder } : {})}
{...(onImportFolderResponse ? { onImportFolderResponse } : {})}
{...(onOpenConnectorsTab ? { onOpenConnectorsTab } : {})}
{...(initialTab ? { initialTab } : {})}
/>

View file

@ -3,6 +3,12 @@
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
vi.mock('@open-design/host', () => ({
isOpenDesignHostAvailable: () => true,
pickAndImportHostProject: vi.fn(),
}));
import { pickAndImportHostProject } from '@open-design/host';
import { NewProjectModal } from '../../src/components/NewProjectModal';
import type {
DesignSystemSummary,
@ -56,6 +62,7 @@ class ResizeObserverMock {
beforeEach(() => {
globalThis.ResizeObserver = ResizeObserverMock as typeof ResizeObserver;
Element.prototype.scrollIntoView = vi.fn();
vi.mocked(pickAndImportHostProject).mockReset();
});
describe('NewProjectModal layout', () => {
@ -116,6 +123,40 @@ describe('NewProjectModal layout', () => {
expect(onClose).toHaveBeenCalledTimes(1);
});
});
it('forwards the desktop folder import response handler to the inner panel', async () => {
const importResult = {
conversationId: 'conversation-host',
entryFile: 'src/App.tsx',
ok: true,
projectId: 'project-host',
} as const;
vi.mocked(pickAndImportHostProject).mockResolvedValue(importResult);
const onImportFolderResponse = vi.fn();
render(
<NewProjectModal
open
skills={skills}
designSystems={designSystems}
defaultDesignSystemId={null}
templates={[]}
promptTemplates={[]}
onCreate={() => {}}
onImportFolderResponse={onImportFolderResponse}
onClose={() => {}}
/>,
);
fireEvent.click(screen.getByRole('button', { name: 'Open folder' }));
await waitFor(() => {
expect(pickAndImportHostProject).toHaveBeenCalledWith({ skillId: 'prototype-skill' });
});
await waitFor(() => {
expect(onImportFolderResponse).toHaveBeenCalledWith(importResult);
});
});
});
describe('NewProjectModal template deletion plumbing', () => {