mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-28 11:41:04 +00:00
18 KiB
18 KiB
WebUI Component Library Extraction Plan
1. Background and Goals
1.1 Background
packages/vscode-ide-companion is a VSCode extension whose core content is a WebView page with UI components provided by React. As the product line expands, more scenarios require building products with Web UI:
- Chrome Browser Extension - Sidebar chat interface
- Web Chat Page - Pure web application
- Conversation Share Page - Render conversations as static HTML
For excellent software engineering architecture, we need to unify and reuse UI components across products.
1.2 Goals
- Extract components from
vscode-ide-companion/src/webview/into an independent@qwen-code/webuipackage - Establish a layered architecture: Pure UI components + Business UI components
- Use Vite + Storybook for development and component showcase
- Abstract platform capabilities through Platform Context for cross-platform reuse
- Provide Tailwind CSS preset to ensure UI consistency across products
2. Current State Analysis
2.1 Current Code Structure
packages/vscode-ide-companion/src/webview/ contains 77 files:
webview/
├── App.tsx # Main entry
├── components/
│ ├── icons/ # 8 icon components
│ ├── layout/ # 8 layout components
│ │ ├── ChatHeader.tsx
│ │ ├── InputForm.tsx
│ │ ├── SessionSelector.tsx
│ │ ├── EmptyState.tsx
│ │ ├── Onboarding.tsx
│ │ └── ...
│ ├── messages/ # Message display components
│ │ ├── UserMessage.tsx
│ │ ├── Assistant/
│ │ ├── MarkdownRenderer/
│ │ ├── ThinkingMessage.tsx
│ │ ├── Waiting/
│ │ └── toolcalls/ # 16 tool call components
│ ├── PermissionDrawer/ # Permission request drawer
│ └── Tooltip.tsx
├── hooks/ # Custom hooks
├── handlers/ # Message handlers
├── styles/ # CSS styles
└── utils/ # Utility functions
2.2 Key Dependency Analysis
Platform Coupling Points:
useVSCodehook - CallsacquireVsCodeApi()for message communicationhandlers/- Handles VSCode message protocol- Some type definitions come from
../types/directory
┌─────────────────────────────────────────────────────────┐
│ App.tsx (Entry) │
├─────────────────────────────────────────────────────────┤
│ hooks/ │ handlers/ │ components/ │
│ ├─useVSCode ◄───┼──────────────────┼──────────────────┤
│ ├─useSession │ ├─MessageRouter │ ├─icons/ │
│ ├─useFileContext│ ├─AuthHandler │ ├─layout/ │
│ └─... │ └─... │ ├─messages/ │
│ │ │ └─PermDrawer/ │
├─────────────────────────────────────────────────────────┤
│ VSCode API (acquireVsCodeApi) │
└─────────────────────────────────────────────────────────┘
3. Target Architecture
3.1 Layered Architecture Design
┌─────────────────────────────────────────────────────────┐
│ Layer 3: Platform Adapters │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │VSCode Adapter│ │Chrome Adapter│ │ Web Adapter │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
├─────────┼────────────────┼────────────────┼────────────┤
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Platform Context Provider │ │
│ └─────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ Layer 2: Chat Components │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ MessageList│ │ ChatHeader │ │ InputForm │ │
│ └────────────┘ └────────────┘ └────────────┘ │
├─────────────────────────────────────────────────────────┤
│ Layer 1: Primitives (Pure UI) │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ Button │ │ Input │ │ Icons │ │Tooltip │ │
│ └────────┘ └────────┘ └────────┘ └────────┘ │
└─────────────────────────────────────────────────────────┘
3.2 Platform Context Design
// @qwen-code/webui/src/context/PlatformContext.ts
interface PlatformContext {
// Message communication
postMessage: (message: unknown) => void;
onMessage: (handler: (message: unknown) => void) => () => void;
// File operations
openFile?: (path: string) => void;
attachFile?: () => void;
// Authentication
login?: () => void;
// Platform info
platform: 'vscode' | 'chrome' | 'web' | 'share';
}
4. Technical Solution
4.1 Build Configuration (Vite Library Mode)
Output formats:
- ESM (
dist/index.js) - Primary format - CJS (
dist/index.cjs) - Compatibility - TypeScript declarations (
dist/index.d.ts)
// vite.config.ts
export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, 'src/index.ts'),
formats: ['es', 'cjs'],
fileName: (format) => `index.${format === 'es' ? 'js' : 'cjs'}`,
},
rollupOptions: {
external: ['react', 'react-dom'],
},
},
});
4.2 Tailwind Preset Solution
// @qwen-code/webui/tailwind.preset.js
module.exports = {
theme: {
extend: {
colors: {
'app-primary': 'var(--app-primary)',
'app-background': 'var(--app-primary-background)',
'app-foreground': 'var(--app-primary-foreground)',
},
},
},
};
// Consumer's tailwind.config.js
module.exports = {
presets: [require('@qwen-code/webui/tailwind.preset')],
content: [
'./src/**/*.{ts,tsx}',
'./node_modules/@qwen-code/webui/dist/**/*.js',
],
};
4.3 Storybook Configuration
packages/webui/
├── .storybook/
│ ├── main.ts # Storybook config
│ ├── preview.ts # Global decorators
│ └── manager.ts # UI config
└── src/
└── stories/ # Story files
5. Component Migration Classification
5.1 Batch 1: No-dependency Components (Ready to migrate)
| Component | Source Path | Complexity | Notes |
|---|---|---|---|
| Icons | components/icons/ |
Low | 8 icon components, pure SVG |
| Tooltip | components/Tooltip.tsx |
Low | Pure UI |
| WaitingMessage | messages/Waiting/ |
Low | Loading state display |
| InterruptedMessage | messages/Waiting/ |
Low | Interrupted state display |
5.2 Batch 2: Light-dependency Components (Need props abstraction)
| Component | Source Path | Dependency | Refactoring |
|---|---|---|---|
| UserMessage | messages/UserMessage.tsx |
onFileClick | Props injection |
| AssistantMessage | messages/Assistant/ |
onFileClick | Props injection |
| ThinkingMessage | messages/ThinkingMessage.tsx |
onFileClick | Props injection |
| MarkdownRenderer | messages/MarkdownRenderer/ |
None | Direct migration |
| EmptyState | layout/EmptyState.tsx |
None | Direct migration |
| ChatHeader | layout/ChatHeader.tsx |
callbacks | Props injection |
5.3 Batch 3: Medium-dependency Components (Need Context)
| Component | Source Path | Dependency | Refactoring |
|---|---|---|---|
| InputForm | layout/InputForm.tsx |
Multiple callbacks | Context + Props |
| SessionSelector | layout/SessionSelector.tsx |
session data | Props injection |
| CompletionMenu | layout/CompletionMenu.tsx |
items data | Props injection |
| PermissionDrawer | PermissionDrawer/ |
callbacks | Context + Props |
| ToolCall components | messages/toolcalls/ |
Various tool displays | Modular migration |
5.4 Batch 4: Heavy-dependency (Keep in platform package)
| Component/Module | Notes |
|---|---|
| App.tsx | Main entry, contains business orchestration logic |
| hooks/ | Most require platform adaptation |
| handlers/ | VSCode message handling |
| Onboarding | Authentication related, platform-specific |
6. Incremental Migration Strategy
6.1 Migration Principles
- Bidirectional compatibility: During migration, vscode-ide-companion can import from both webui and local
- One-by-one replacement: For each migrated component, replace import path in VSCode extension and verify
- No breaking changes: Ensure the extension builds and runs normally after each migration
6.2 Migration Workflow
Developer ──► @qwen-code/webui ──► vscode-ide-companion
│ │ │
│ 1. Copy component to webui │
│ 2. Add Story for verification │
│ 3. Export from index.ts │
│ │ │
│ └──────────────────────┤
│ │
│ 4. Update import path
│ 5. Delete original component
│ 6. Build and test
6.3 Example: Migrating Icons
// Before: vscode-ide-companion/src/webview/components/icons/index.ts
export { FileIcon } from './FileIcons.js';
// After: Update import
import { FileIcon } from '@qwen-code/webui';
// or import { FileIcon } from '@qwen-code/webui/icons';
7. Task Breakdown
Phase 0: Infrastructure Setup (Prerequisites)
- T0-1: Vite build configuration
- T0-2: Storybook configuration
- T0-3: Tailwind preset creation
- T0-4: Platform Context definition
- T0-5: Shared types migration
Phase 1: Pure UI Components Migration
- T1-1: Icons components migration (8 files)
- T1-2: Tooltip component migration
- T1-3: WaitingMessage / InterruptedMessage migration
- T1-4: Basic Button/Input components refinement
Phase 2: Message Components Migration
- T2-1: MarkdownRenderer migration
- T2-2: UserMessage migration
- T2-3: AssistantMessage migration
- T2-4: ThinkingMessage migration
Phase 3: Layout Components Migration
- T3-1: ChatHeader migration
- T3-2: EmptyState migration
- T3-3: InputForm migration (requires Context)
- T3-4: SessionSelector migration
- T3-5: CompletionMenu migration
Phase 4: Complex Components Migration
- T4-1: PermissionDrawer migration
- T4-2: ToolCall series components migration (16 files)
Phase 5: Platform Adapters
- T5-1: VSCode Adapter implementation
- T5-2: Chrome Extension Adapter
- T5-3: Web/Share Page Adapter
8. Risks and Considerations
8.1 Common Pitfalls
-
Tailwind Class Name Tree Shaking
- Problem: Tailwind class names may be removed after library bundling
- Solution: Consumer's
contentconfig needs to includenode_modules/@qwen-code/webui
-
CSS Variable Scope
- Problem: Variables like
var(--app-primary)need to be defined by consumers - Solution: Provide default CSS variables file, or define fallbacks in Tailwind preset
- Problem: Variables like
-
React Version Compatibility
- Current vscode-ide-companion uses React 19, webui's peerDependencies is React 18
- Need to update peerDependencies to
"react": "^18.0.0 || ^19.0.0"
-
ESM/CJS Compatibility
- VSCode extensions may require CJS format
- Vite needs to be configured for dual format output
8.2 Industry References
- Radix UI: Pure Headless components, styles completely controlled by consumers
- shadcn/ui: Copy components into project, rather than importing as dependency
- Ant Design: Complete component library, customization through ConfigProvider
8.3 Acceptance Criteria
Each migration task completion requires:
- Component has corresponding Storybook Story
- Import in vscode-ide-companion has been updated
- Extension builds successfully (
npm run build:vscode) - Extension functionality works (manual testing or existing tests pass)
9. Time Estimation
| Phase | Tasks | Estimated Days | Parallelizable |
|---|---|---|---|
| Phase 0 | 5 | 2-3 days | Partially |
| Phase 1 | 4 | 1-2 days | Fully |
| Phase 2 | 4 | 2-3 days | Fully |
| Phase 3 | 5 | 3-4 days | Partially |
| Phase 4 | 2 | 3-4 days | Yes |
| Phase 5 | 3 | 2-3 days | Yes |
Total: Approximately 13-19 person-days (sequential execution), can be reduced to 1-2 weeks with parallel work
10. Development and Debugging Workflow
10.1 Component Development Flow
┌─────────────────────────────────────────────────────────────────┐
│ Development Workflow │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. Develop/Modify Component │
│ └── Edit files in @qwen-code/webui/src/ │
│ │
│ 2. Debug with Storybook │
│ └── npm run storybook (port 6006) │
│ └── View component in isolation │
│ └── Test different props/states │
│ │
│ 3. Build Library │
│ └── npm run build │
│ └── Outputs: dist/index.js, dist/index.cjs, dist/index.d.ts │
│ │
│ 4. Use in VSCode Extension │
│ └── import { Component } from '@qwen-code/webui' │
│ └── No UI code modifications in vscode-ide-companion │
│ │
└─────────────────────────────────────────────────────────────────┘
10.2 Debugging Commands
# Start Storybook for component development
cd packages/webui
npm run storybook
# Watch mode for library development
npm run dev
# Build library for production
npm run build
# Type checking
npm run typecheck
10.3 Key Principles
- Single Source of Truth: All UI components live in
@qwen-code/webui - Storybook First: Debug and validate components in Storybook before integration
- No UI Code in Consumers:
vscode-ide-companiononly imports and uses components - Platform Abstraction: Use
PlatformContextfor platform-specific behaviors