mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
* feat(daemon): add project working directory management and editor hand-off functionality - Introduced new flags for project commands to manage working directories, including `--working-dir` and `--dir`. - Implemented API routes for listing available editors and opening projects in selected editors. - Added a hand-off button in the ChatPane header to facilitate opening project folders in local applications. - Enhanced the HomeHero component to include working directory and design system settings, improving user experience in project creation. - Created HomeHeroSettingsChips component for inline management of working directory and design system selection. * feat(chat): implement voice transcription proxy and enhance UI components - Added a new API route for voice transcription using OpenAI's `/audio/transcriptions` endpoint, allowing users to send audio blobs directly for transcription. - Integrated multer for handling audio file uploads in memory, ensuring efficient processing without disk storage. - Updated the HomeHero component to include example prompt suggestions for plugins, enhancing user interaction. - Introduced the EditorIcon component to visually represent different editors in the hand-off menu, improving the user experience. - Refined the HandoffButton component to utilize the new EditorIcon, providing a more cohesive interface for selecting editors. - Enhanced CSS styles for various components to improve layout and responsiveness, including adjustments to tab and button sizes for better usability. * style(workspace-shell): enhance layout and overflow handling - Updated CSS for .workspace-shell to ensure full viewport width and height, with proper overflow management. - Adjusted grid layout to prevent content overflow and maintain responsiveness. - Modified styles for .workspace-tabs-chrome to improve width handling and prevent overflow issues. * refactor(chat): remove voice transcription proxy and related components - Deleted the voice transcription proxy implementation, including the associated API route and multer configuration. - Removed the MicButton component from the ChatComposer and HomeHero components to streamline the UI. - Updated HomeHero to include example suggestions without the voice input functionality. - Adjusted CSS styles for various components to maintain layout consistency after the removal of the MicButton. * feat(daemon): implement minting of HMAC tokens for working directory management - Added a new function `mintImportTokenFromCurrentSecret` to generate HMAC tokens bound to a specified base directory, enhancing security for working directory operations. - Updated the `desktop-auth.ts` file to include the new token minting functionality, which returns structured errors when the desktop auth secret is cleared. - Introduced new IPC message types for minting import tokens in the sidecar protocol, allowing seamless integration with the daemon's working directory management. - Enhanced the `WorkingDirPill` component to utilize the new token minting flow for secure directory selection in desktop builds. - Updated CSS styles for the HomeHero component to accommodate new example suggestion features and maintain layout consistency. * fix(HomeView): import HOME_HERO_CHIPS constant for improved chip management - Updated the HomeView component to import the HOME_HERO_CHIPS constant from the chips module, enhancing the management of hero chips within the component. * feat(daemon): implement mintImportTokenViaSidecar for secure working directory management - Introduced the `mintImportTokenViaSidecar` function to facilitate the minting of HMAC tokens for desktop-import operations via the daemon's sidecar IPC. This allows CLI commands to bypass authentication when the desktop-auth gate is active. - Updated the CLI to utilize the new token minting function when setting the working directory, ensuring secure access to trust-gated API endpoints. - Enhanced the sidecar server to handle minting requests and return structured error messages for improved user feedback. - Added tests to validate the new token minting functionality and its integration with the working directory management process. - Refactored related components to support the new token flow, improving overall security and user experience. * feat(HomeHero): enhance UI components and styles for improved user experience - Updated HomeHero component to replace active dot indicators with Plug icons for better visual representation of active plugins. - Adjusted CSS styles for various elements, including padding and dimensions, to enhance layout consistency and responsiveness. - Introduced new styles for active type icons and improved hover effects for buttons. - Updated HomeHeroSettingsChips to change button titles and icons for clarity. - Added tests to ensure proper rendering and functionality of updated components. * feat(ProjectDesignSystemPicker): enhance design system selection with preview functionality - Updated the ProjectDesignSystemPicker component to include a preview feature for design systems, allowing users to see a preview of the selected design system. - Implemented hover functionality to update the preview based on the hovered design system. - Added fullscreen preview capability for a more immersive experience. - Enhanced CSS styles for the design system picker to improve layout and responsiveness. - Introduced tests to validate the new preview functionality and ensure proper interaction within the component. * feat: refactor project metadata handling and enhance design system picker - Updated the default scenario plugin ID retrieval to use project metadata, improving the logic for determining the appropriate plugin based on project intent. - Enhanced the ProjectDesignSystemPicker and related components to support localized design system summaries and categories, improving user experience. - Introduced new translations for working directory and design system picker components, ensuring better accessibility and usability across different locales. - Added a new 'live-artifact' project type to the HomeHero chips, expanding the functionality for users creating refreshable artifacts. - Updated tests to validate the new project metadata handling and design system picker functionalities. * feat: enhance localization and styling for design system components - Added French translations for working directory and design system picker components, improving accessibility for French-speaking users. - Updated CSS styles for the pet task item to ensure consistent padding and layout. - Introduced a new test suite for HomeHeroSettingsChips to validate localization and design system selection functionality. - Enhanced ProjectDesignSystemPicker tests to ensure proper localization and interaction with design system categories. * fix: update .gitignore to include all claude-sessions directories and remove specific session files - Modified .gitignore to ensure all claude-sessions directories are ignored by using a wildcard pattern. - Deleted two specific claude-sessions markdown files to clean up unnecessary session data. * fix: repair home automation ci regressions * fix: stabilize artifact consistency e2e * Remove folder picker changes from PR 2400 --------- Co-authored-by: pftom <1043269994@qq.com> Co-authored-by: qiongyu1999 <2694684348@qq.com>
207 lines
5.9 KiB
TypeScript
207 lines
5.9 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import { cleanup, render, screen } from '@testing-library/react';
|
|
import { forwardRef } from 'react';
|
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import { ChatPane } from '../../src/components/ChatPane';
|
|
import { DESIGN_SYSTEM_WORKSPACE_PROMPT_PREFIX } from '../../src/design-system-auto-prompt';
|
|
import type { ChatMessage, Conversation, ProjectMetadata } from '../../src/types';
|
|
|
|
vi.mock('../../src/i18n', () => ({
|
|
useI18n: () => ({
|
|
locale: 'en',
|
|
setLocale: () => undefined,
|
|
t: (key: string) => key,
|
|
}),
|
|
useT: () => (key: string) => key,
|
|
}));
|
|
|
|
vi.mock('../../src/components/AssistantMessage', () => ({
|
|
AssistantMessage: ({ streaming, message }: { streaming: boolean; message: ChatMessage }) => (
|
|
<output data-testid={`assistant-streaming-${message.id}`}>{streaming ? 'streaming' : 'idle'}</output>
|
|
),
|
|
}));
|
|
|
|
vi.mock('../../src/components/ChatComposer', () => ({
|
|
ChatComposer: forwardRef(({ streaming }: { streaming: boolean }, _ref) => (
|
|
<output data-testid="composer-streaming">{streaming ? 'streaming' : 'idle'}</output>
|
|
)),
|
|
}));
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
});
|
|
|
|
describe('ChatPane streaming state', () => {
|
|
it('renders user turns with the chat bubble styling hook', () => {
|
|
const messages: ChatMessage[] = [
|
|
{
|
|
id: 'user-1',
|
|
role: 'user',
|
|
content: 'Generate a simple sign-in page',
|
|
createdAt: 1,
|
|
},
|
|
];
|
|
|
|
render(
|
|
<ChatPane
|
|
messages={messages}
|
|
streaming={false}
|
|
error={null}
|
|
projectId="project-1"
|
|
projectFiles={[]}
|
|
onEnsureProject={async () => 'project-1'}
|
|
onSend={vi.fn()}
|
|
onStop={vi.fn()}
|
|
conversations={conversations}
|
|
activeConversationId="conv-1"
|
|
onSelectConversation={vi.fn()}
|
|
onDeleteConversation={vi.fn()}
|
|
projectMetadata={projectMetadata}
|
|
/>,
|
|
);
|
|
|
|
const bubble = screen.getByText('Generate a simple sign-in page');
|
|
expect(bubble.classList.contains('user-bubble')).toBe(true);
|
|
expect(bubble.closest('.msg.user')).not.toBeNull();
|
|
});
|
|
|
|
it('summarizes auto-sent design-system workspace prompts', () => {
|
|
const messages: ChatMessage[] = [
|
|
{
|
|
id: 'user-1',
|
|
role: 'user',
|
|
content: `${DESIGN_SYSTEM_WORKSPACE_PROMPT_PREFIX}
|
|
Use the files in this project as the design system source for future projects.
|
|
Expected output:
|
|
- A clear DESIGN.md with all generated rules.`,
|
|
createdAt: 1,
|
|
},
|
|
];
|
|
|
|
render(
|
|
<ChatPane
|
|
messages={messages}
|
|
streaming={false}
|
|
error={null}
|
|
projectId="project-1"
|
|
projectFiles={[]}
|
|
onEnsureProject={async () => 'project-1'}
|
|
onSend={vi.fn()}
|
|
onStop={vi.fn()}
|
|
conversations={conversations}
|
|
activeConversationId="conv-1"
|
|
onSelectConversation={vi.fn()}
|
|
onDeleteConversation={vi.fn()}
|
|
projectMetadata={projectMetadata}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByText('Creating design system workspace')).toBeTruthy();
|
|
expect(screen.queryByText(DESIGN_SYSTEM_WORKSPACE_PROMPT_PREFIX, { exact: false })).toBeNull();
|
|
expect(screen.queryByRole('button', { name: 'chat.copyPrompt' })).toBeNull();
|
|
});
|
|
|
|
it('keeps composer idle while active-run messages still render as streaming', () => {
|
|
const messages: ChatMessage[] = [
|
|
{
|
|
id: 'assistant-1',
|
|
role: 'assistant',
|
|
content: 'still running',
|
|
createdAt: 1,
|
|
runId: 'run-1',
|
|
runStatus: 'running',
|
|
},
|
|
];
|
|
|
|
render(
|
|
<ChatPane
|
|
messages={messages}
|
|
streaming={false}
|
|
error={null}
|
|
projectId="project-1"
|
|
projectFiles={[]}
|
|
onEnsureProject={async () => 'project-1'}
|
|
onSend={vi.fn()}
|
|
onStop={vi.fn()}
|
|
conversations={conversations}
|
|
activeConversationId="conv-1"
|
|
onSelectConversation={vi.fn()}
|
|
onDeleteConversation={vi.fn()}
|
|
projectMetadata={projectMetadata}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByTestId('composer-streaming').textContent).toBe('idle');
|
|
expect(screen.getByTestId('assistant-streaming-assistant-1').textContent).toBe('streaming');
|
|
});
|
|
|
|
it('renders a stopped pinned todo after a terminal run without a final TodoWrite', () => {
|
|
const messages: ChatMessage[] = [
|
|
{
|
|
id: 'assistant-1',
|
|
role: 'assistant',
|
|
content: '',
|
|
createdAt: 1,
|
|
startedAt: 1,
|
|
endedAt: 2,
|
|
runStatus: 'failed',
|
|
events: [
|
|
{
|
|
kind: 'tool_use',
|
|
id: 'todo-1',
|
|
name: 'TodoWrite',
|
|
input: {
|
|
todos: [
|
|
{
|
|
content: 'Build prototype',
|
|
status: 'in_progress',
|
|
activeForm: 'Building prototype',
|
|
},
|
|
{ content: 'Run QA', status: 'pending' },
|
|
],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
const { container } = render(
|
|
<ChatPane
|
|
messages={messages}
|
|
streaming={false}
|
|
error={null}
|
|
projectId="project-1"
|
|
projectFiles={[]}
|
|
onEnsureProject={async () => 'project-1'}
|
|
onSend={vi.fn()}
|
|
onStop={vi.fn()}
|
|
conversations={conversations}
|
|
activeConversationId="conv-1"
|
|
onSelectConversation={vi.fn()}
|
|
onDeleteConversation={vi.fn()}
|
|
projectMetadata={projectMetadata}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByText('0/2')).toBeTruthy();
|
|
expect(container.querySelector('.todo-stopped')?.textContent).toContain('Build prototype');
|
|
expect(container.querySelector('.todo-in_progress')).toBeNull();
|
|
expect(container.querySelector('.op-todo-current')).toBeNull();
|
|
});
|
|
});
|
|
|
|
const conversations: Conversation[] = [
|
|
{
|
|
id: 'conv-1',
|
|
projectId: 'project-1',
|
|
title: 'Conversation 1',
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
},
|
|
];
|
|
|
|
const projectMetadata: ProjectMetadata = {
|
|
kind: 'prototype',
|
|
};
|