diff --git a/packages/webui/.storybook/main.ts b/packages/webui/.storybook/main.ts index 69c9da990..56d9c536c 100644 --- a/packages/webui/.storybook/main.ts +++ b/packages/webui/.storybook/main.ts @@ -27,5 +27,18 @@ const config: StorybookConfig = { getAbsolutePath('@storybook/addon-onboarding'), ], framework: getAbsolutePath('@storybook/react-vite'), + // Set ChatViewer Playground as default story when Storybook opens + managerHead: (head) => ` + ${head} + + `, }; export default config; diff --git a/packages/webui/.storybook/preview.css b/packages/webui/.storybook/preview.css index 1514fc9fd..f62d143ed 100644 --- a/packages/webui/.storybook/preview.css +++ b/packages/webui/.storybook/preview.css @@ -6,6 +6,9 @@ /* Import CSS variables BEFORE Tailwind so they're available */ @import '../src/styles/variables.css'; +/* Import component styles */ +@import '../src/styles/components.css'; +@import '../src/styles/timeline.css'; @tailwind base; @tailwind components; @@ -14,4 +17,26 @@ /* Ensure text color is inherited properly in Storybook */ body { color: var(--app-primary-foreground); + margin: 0; + padding: 0; +} + +/* Storybook container styling */ +.storybook-container { + box-sizing: border-box; +} + +/* Fix Storybook iframe height for full-height stories */ +#storybook-root { + height: 100%; +} + +/* Ensure stories with large content don't overflow awkwardly */ +.sb-show-main.sb-main-padded { + padding: 0 !important; +} + +/* Full height stories should fill the iframe */ +[data-story-block='true'] { + height: 100%; } diff --git a/packages/webui/.storybook/preview.ts b/packages/webui/.storybook/preview.ts index 47686b067..8a9ad79cb 100644 --- a/packages/webui/.storybook/preview.ts +++ b/packages/webui/.storybook/preview.ts @@ -23,22 +23,42 @@ const preview: Preview = { { name: 'light', value: '#ffffff' }, ], }, + layout: 'fullscreen', + options: { + // Set ChatViewer Playground as the default story + storySort: { + order: ['Chat', ['ChatViewer', ['Playground', '*']], '*'], + }, + }, }, decorators: [ - (Story) => - React.createElement( + (Story, context) => { + // For ChatViewer stories, use full height container + const isFullHeight = + context.title?.includes('ChatViewer') || + context.parameters?.fullHeight === true; + + return React.createElement( 'div', { + className: 'storybook-container', style: { backgroundColor: 'var(--app-background)', color: 'var(--app-primary-foreground)', - minHeight: '100px', - padding: '16px', + height: isFullHeight ? '100vh' : 'auto', + minHeight: isFullHeight ? '100vh' : '100px', + padding: isFullHeight ? '0' : '16px', + overflow: isFullHeight ? 'hidden' : 'visible', }, }, React.createElement(Story), - ), + ); + }, ], + // Set initial path to ChatViewer Playground + initialGlobals: { + backgrounds: { value: 'dark' }, + }, }; export default preview; diff --git a/packages/webui/README.md b/packages/webui/README.md index 0558d6d4f..d69937b42 100644 --- a/packages/webui/README.md +++ b/packages/webui/README.md @@ -1,4 +1,4 @@ -# @anthropic/webui +# @qwen-code/webui A shared React component library for Qwen Code applications, providing cross-platform UI components with consistent styling and behavior. @@ -13,14 +13,14 @@ A shared React component library for Qwen Code applications, providing cross-pla ## Installation ```bash -npm install @anthropic/webui +npm install @qwen-code/webui ``` ## Quick Start ```tsx -import { Button, Input, Tooltip } from '@anthropic/webui'; -import { PlatformProvider } from '@anthropic/webui/context'; +import { Button, Input, Tooltip } from '@qwen-code/webui'; +import { PlatformProvider } from '@qwen-code/webui/context'; function App() { return ( @@ -40,7 +40,7 @@ function App() { #### Button ```tsx -import { Button } from '@anthropic/webui'; +import { Button } from '@qwen-code/webui'; - - - {/* Example of using shared Input component */} -
- -
- - {/* Example of using shared Message component */} -
- -
- - {/* Example of using shared Tooltip component */} -
- - - -
- - {/* Example of using shared PermissionDrawer component */} - setShowPermissionDrawer(false)} - onConfirm={handleConfirmPermission} - permissions={[ - 'Access browser history', - 'Read current page', - 'Capture screenshots', - ]} - /> - - ); -}; - -export default ExampleComponent; diff --git a/packages/webui/src/components/PermissionDrawer.css b/packages/webui/src/components/PermissionDrawer.css new file mode 100644 index 000000000..734676905 --- /dev/null +++ b/packages/webui/src/components/PermissionDrawer.css @@ -0,0 +1,249 @@ +/** + * @license + * Copyright 2025 Qwen Team + * SPDX-License-Identifier: Apache-2.0 + * + * PermissionDrawer component styles + * These classes can be used instead of inline styles + */ + +/* Permission Request Card */ +.permission-request-card { + background: var(--app-input-background); + border: 1px solid var(--app-primary, #3b82f6); + border-radius: var(--app-radius-md, 6px); + margin: var(--app-spacing-medium, 8px) 0; + margin-bottom: var(--app-spacing-lg, 16px); + overflow: visible; + animation: fadeIn 0.2s ease-in; +} + +.permission-card-body { + padding: var(--app-spacing-lg, 12px); + min-height: fit-content; + height: auto; +} + +.permission-header { + display: flex; + align-items: center; + gap: var(--app-spacing-lg, 12px); + margin-bottom: var(--app-spacing-lg, 12px); +} + +.permission-icon-wrapper { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + background: rgba(59, 130, 246, 0.1); + border-radius: var(--app-radius-md, 6px); + flex-shrink: 0; +} + +.permission-icon { + font-size: 20px; +} + +.permission-info { + flex: 1; + min-width: 0; +} + +.permission-title { + font-weight: 600; + color: var(--app-primary-foreground); + margin-bottom: 2px; +} + +.permission-subtitle { + font-size: 12px; + color: var(--app-secondary-foreground); +} + +.permission-command-section { + margin-bottom: var(--app-spacing-lg, 12px); +} + +.permission-command-label { + font-size: 11px; + font-weight: 600; + color: var(--app-secondary-foreground); + margin-bottom: var(--app-spacing-sm, 4px); + text-transform: uppercase; +} + +.permission-command-code { + display: block; + font-family: var(--app-font-mono); + font-size: var(--app-monospace-font-size, 13px); + color: var(--app-primary-foreground); + background: var(--app-primary-background); + padding: var(--app-spacing-medium, 8px); + border-radius: var(--app-radius-sm, 4px); + overflow-x: auto; + white-space: pre-wrap; + word-break: break-word; +} + +.permission-locations-section { + margin-bottom: var(--app-spacing-lg, 12px); +} + +.permission-locations-label { + font-size: 11px; + font-weight: 600; + color: var(--app-secondary-foreground); + margin-bottom: var(--app-spacing-sm, 4px); + text-transform: uppercase; +} + +.permission-location-item { + display: flex; + align-items: center; + gap: var(--app-spacing-sm, 4px); + padding: var(--app-spacing-sm, 4px) 0; + font-size: 12px; +} + +.permission-location-icon { + flex-shrink: 0; +} + +.permission-location-path { + color: var(--app-primary-foreground); + font-family: var(--app-font-mono); +} + +.permission-location-line { + color: var(--app-secondary-foreground); +} + +.permission-options-section { + margin-top: var(--app-spacing-lg, 12px); +} + +.permission-options-label { + font-size: 12px; + font-weight: 500; + color: var(--app-primary-foreground); + margin-bottom: var(--app-spacing-medium, 8px); +} + +.permission-options-list { + display: flex; + flex-direction: column; + gap: var(--app-spacing-sm, 4px); +} + +.permission-option { + display: flex; + align-items: center; + gap: var(--app-spacing-medium, 8px); + padding: var(--app-spacing-medium, 8px) var(--app-spacing-lg, 12px); + background: var(--app-primary-background); + border: 1px solid var(--app-input-border); + border-radius: var(--app-radius-sm, 4px); + cursor: pointer; + transition: all 0.15s ease; +} + +.permission-option:hover { + background: var(--app-list-hover-background); + border-color: var(--app-primary, #3b82f6); +} + +.permission-option.selected { + border-color: var(--app-primary, #3b82f6); + background: rgba(59, 130, 246, 0.1); +} + +.permission-radio { + flex-shrink: 0; +} + +.permission-option-content { + display: flex; + align-items: center; + gap: var(--app-spacing-sm, 4px); + flex: 1; +} + +.permission-option-number { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 20px; + height: 20px; + padding: 0 6px; + font-size: 11px; + font-weight: 600; + color: var(--app-secondary-foreground); + background-color: var(--app-list-hover-background); + border-radius: 4px; + margin-right: 4px; +} + +.permission-option.selected .permission-option-number { + color: white; + background-color: var(--app-primary, #3b82f6); +} + +.permission-always-badge { + font-size: 12px; +} + +.permission-no-options { + text-align: center; + padding: var(--app-spacing-lg, 12px); + color: var(--app-secondary-foreground); +} + +.permission-actions { + margin-top: var(--app-spacing-lg, 12px); + display: flex; + justify-content: flex-end; +} + +.permission-confirm-button { + padding: var(--app-spacing-medium, 8px) var(--app-spacing-lg, 16px); + background: var(--app-primary, #3b82f6); + color: white; + border: none; + border-radius: var(--app-radius-sm, 4px); + font-size: 13px; + font-weight: 500; + cursor: pointer; + transition: filter 0.15s ease; +} + +.permission-confirm-button:hover:not(:disabled) { + filter: brightness(1.1); +} + +.permission-confirm-button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.permission-success { + display: flex; + align-items: center; + justify-content: center; + gap: var(--app-spacing-medium, 8px); + padding: var(--app-spacing-lg, 12px); + background: rgba(16, 185, 129, 0.1); + border-radius: var(--app-radius-sm, 4px); + margin-top: var(--app-spacing-lg, 12px); +} + +.permission-success-icon { + color: var(--app-success, #10b981); + font-weight: bold; +} + +.permission-success-text { + color: var(--app-success, #10b981); + font-size: 13px; +}