mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
feat(vscode-ide-companion): 更新 UI 样式
- 重构 PlanDisplay 组件和样式 - 更新 PermissionRequest 组件逻辑 - 增强 PermissionDrawer 样式,提升视觉体验 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -154,3 +154,319 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* ===========================================
|
||||
Permission Request Card Styles
|
||||
=========================================== */
|
||||
|
||||
.permission-request-card {
|
||||
background: var(--app-primary-background);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.permission-card-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
/* Permission Header */
|
||||
.permission-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.permission-icon-wrapper {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.permission-icon {
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.permission-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.permission-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--app-primary-foreground);
|
||||
line-height: 1.5;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.permission-subtitle {
|
||||
font-size: 12px;
|
||||
color: var(--app-secondary-foreground);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Command Section - Bash style */
|
||||
.permission-command-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
background: var(--app-secondary-background);
|
||||
border: 1px solid var(--app-transparent-inner-border);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.permission-command-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
background: var(--app-secondary-background);
|
||||
border-bottom: 1px solid var(--app-transparent-inner-border);
|
||||
}
|
||||
|
||||
.permission-command-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.permission-command-dot {
|
||||
color: var(--app-qwen-orange, #ff8c00);
|
||||
font-size: 8px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.permission-command-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: var(--app-secondary-foreground);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.permission-command-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.permission-command-input-section {
|
||||
display: grid;
|
||||
grid-template-columns: 32px 1fr;
|
||||
align-items: flex-start;
|
||||
padding: 8px 0;
|
||||
background: var(--app-primary-background);
|
||||
}
|
||||
|
||||
.permission-command-io-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: var(--app-secondary-foreground);
|
||||
opacity: 0.6;
|
||||
text-align: right;
|
||||
padding-right: 12px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.permission-command-code {
|
||||
font-family: var(--vscode-editor-font-family, 'Monaco', 'Courier New', monospace);
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
color: var(--app-primary-foreground);
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0 12px 0 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.permission-command-description {
|
||||
font-size: 12px;
|
||||
color: var(--app-secondary-foreground);
|
||||
opacity: 0.7;
|
||||
padding: 8px 12px;
|
||||
border-top: 1px solid var(--app-transparent-inner-border);
|
||||
background: var(--app-secondary-background);
|
||||
}
|
||||
|
||||
/* Locations Section */
|
||||
.permission-locations-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.permission-locations-label {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--app-secondary-foreground);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.permission-location-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 8px;
|
||||
background: var(--app-secondary-background);
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.permission-location-icon {
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.permission-location-path {
|
||||
flex: 1;
|
||||
color: var(--app-primary-foreground);
|
||||
font-family: var(--vscode-editor-font-family, 'Monaco', 'Courier New', monospace);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.permission-location-line {
|
||||
color: var(--app-secondary-foreground);
|
||||
opacity: 0.6;
|
||||
font-family: var(--vscode-editor-font-family, 'Monaco', 'Courier New', monospace);
|
||||
}
|
||||
|
||||
/* Options Section */
|
||||
.permission-options-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.permission-options-label {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--app-secondary-foreground);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.permission-options-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.permission-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
background: var(--app-secondary-background);
|
||||
border: 1px solid var(--app-transparent-inner-border);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.permission-option:hover {
|
||||
background: var(--app-ghost-button-hover-background);
|
||||
border-color: var(--app-primary-border-color);
|
||||
}
|
||||
|
||||
.permission-option.selected {
|
||||
background: var(--app-qwen-clay-button-orange);
|
||||
border-color: var(--app-qwen-orange, #ff8c00);
|
||||
}
|
||||
|
||||
.permission-radio {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.permission-option-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 13px;
|
||||
color: var(--app-primary-foreground);
|
||||
}
|
||||
|
||||
.permission-option-number {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: var(--app-qwen-orange, #ff8c00);
|
||||
color: var(--app-qwen-ivory, #fff);
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.permission-always-badge {
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.permission-no-options {
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
color: var(--app-secondary-foreground);
|
||||
opacity: 0.6;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Actions */
|
||||
.permission-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.permission-confirm-button {
|
||||
padding: 8px 16px;
|
||||
background: var(--app-qwen-orange, #ff8c00);
|
||||
color: var(--app-qwen-ivory, #fff);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.permission-confirm-button:hover:not(:disabled) {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.permission-confirm-button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Success Message */
|
||||
.permission-success {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px;
|
||||
background: var(--app-qwen-green, #6BCF7F);
|
||||
color: var(--app-qwen-ivory, #fff);
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.permission-success-icon {
|
||||
font-size: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.permission-success-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@@ -117,11 +117,26 @@ export const PermissionRequest: React.FC<PermissionRequestProps> = ({
|
||||
{/* Show command if available */}
|
||||
{(toolCall.rawInput?.command || toolCall.title) && (
|
||||
<div className="permission-command-section">
|
||||
<div className="permission-command-label">Command</div>
|
||||
<div className="permission-command-header">
|
||||
<div className="permission-command-status">
|
||||
<span className="permission-command-dot">●</span>
|
||||
<span className="permission-command-label">COMMAND</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="permission-command-content">
|
||||
<div className="permission-command-input-section">
|
||||
<span className="permission-command-io-label">IN</span>
|
||||
<code className="permission-command-code">
|
||||
{toolCall.rawInput?.command || toolCall.title}
|
||||
</code>
|
||||
</div>
|
||||
{toolCall.rawInput?.description && (
|
||||
<div className="permission-command-description">
|
||||
{toolCall.rawInput.description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Show file locations if available */}
|
||||
|
||||
@@ -6,65 +6,54 @@
|
||||
|
||||
/**
|
||||
* PlanDisplay.css - Styles for the task plan component
|
||||
* Simple, clean timeline-style design
|
||||
* Clean checklist-style design matching Claude Code CLI
|
||||
*/
|
||||
|
||||
.plan-display {
|
||||
background: var(--app-secondary-background);
|
||||
border: 1px solid var(--app-transparent-inner-border);
|
||||
border-radius: var(--corner-radius-medium);
|
||||
padding: 16px;
|
||||
margin: 12px 0;
|
||||
animation: fadeIn 0.3s ease-in;
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.plan-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
color: var(--app-primary-foreground);
|
||||
gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.plan-header-icon {
|
||||
.plan-progress-icons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.plan-progress-icon {
|
||||
flex-shrink: 0;
|
||||
opacity: 0.8;
|
||||
color: var(--app-secondary-foreground);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.plan-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--app-primary-foreground);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: var(--app-secondary-foreground);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.plan-entries {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
position: relative;
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
.plan-entry {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
padding: 8px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Vertical line on the left */
|
||||
.plan-entry-line {
|
||||
position: absolute;
|
||||
left: 7px;
|
||||
top: 24px;
|
||||
bottom: -8px;
|
||||
width: 2px;
|
||||
background: var(--app-qwen-clay-button-orange);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.plan-entry:last-child .plan-entry-line {
|
||||
display: none;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 3px 0;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
/* Icon container */
|
||||
@@ -73,60 +62,62 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.plan-icon {
|
||||
display: block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
/* 不同状态的图标颜色 */
|
||||
.plan-icon.pending {
|
||||
color: var(--app-secondary-foreground);
|
||||
opacity: 0.35;
|
||||
}
|
||||
|
||||
.plan-icon.in-progress {
|
||||
color: var(--app-secondary-foreground);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.plan-icon.completed {
|
||||
color: #4caf50; /* 绿色勾号 */
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* Content */
|
||||
.plan-entry-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
min-height: 24px;
|
||||
padding-top: 1px;
|
||||
}
|
||||
|
||||
.plan-entry-number {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--app-secondary-foreground);
|
||||
flex-shrink: 0;
|
||||
min-width: 24px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.plan-entry-text {
|
||||
flex: 1;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
color: var(--app-primary-foreground);
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
/* Status-specific styles */
|
||||
.plan-entry.completed .plan-entry-text {
|
||||
opacity: 0.6;
|
||||
opacity: 0.5;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.plan-entry.in_progress .plan-entry-text {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.plan-entry.in_progress .plan-entry-number {
|
||||
color: var(--app-qwen-orange);
|
||||
font-weight: 600;
|
||||
font-weight: 400;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-8px);
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
|
||||
@@ -21,77 +21,67 @@ interface PlanDisplayProps {
|
||||
* PlanDisplay component - displays AI's task plan/todo list
|
||||
*/
|
||||
export const PlanDisplay: React.FC<PlanDisplayProps> = ({ entries }) => {
|
||||
const getStatusIcon = (status: string, _index: number) => {
|
||||
// 计算完成进度
|
||||
const completedCount = entries.filter((e) => e.status === 'completed').length;
|
||||
const totalCount = entries.length;
|
||||
|
||||
const getStatusIcon = (status: string) => {
|
||||
switch (status) {
|
||||
case 'in_progress':
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
className="plan-icon in-progress"
|
||||
>
|
||||
<rect
|
||||
x="2"
|
||||
y="2"
|
||||
width="12"
|
||||
height="12"
|
||||
rx="2"
|
||||
fill="var(--app-qwen-orange)"
|
||||
/>
|
||||
<path
|
||||
d="M7 4L7 12M10 8L4 8"
|
||||
stroke="var(--app-qwen-ivory)"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case 'completed':
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
className="plan-icon completed"
|
||||
>
|
||||
<rect
|
||||
x="2"
|
||||
y="2"
|
||||
width="12"
|
||||
height="12"
|
||||
rx="2"
|
||||
fill="var(--app-qwen-green, #6BCF7F)"
|
||||
/>
|
||||
<circle cx="7" cy="7" r="6" fill="currentColor" opacity="0.2" />
|
||||
<path
|
||||
d="M5 8L7 10L11 6"
|
||||
stroke="var(--app-qwen-ivory)"
|
||||
d="M4 7.5L6 9.5L10 4.5"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
default:
|
||||
case 'in_progress':
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
className="plan-icon in-progress"
|
||||
>
|
||||
<circle
|
||||
cx="7"
|
||||
cy="7"
|
||||
r="5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2.5"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
default:
|
||||
// pending
|
||||
return (
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
className="plan-icon pending"
|
||||
>
|
||||
<rect
|
||||
x="2.5"
|
||||
y="2.5"
|
||||
width="11"
|
||||
height="11"
|
||||
rx="2"
|
||||
stroke="var(--app-secondary-foreground)"
|
||||
<circle
|
||||
cx="7"
|
||||
cy="7"
|
||||
r="5.5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1"
|
||||
fill="transparent"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -101,41 +91,49 @@ export const PlanDisplay: React.FC<PlanDisplayProps> = ({ entries }) => {
|
||||
return (
|
||||
<div className="plan-display">
|
||||
<div className="plan-header">
|
||||
<div className="plan-progress-icons">
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
className="plan-header-icon"
|
||||
>
|
||||
<rect
|
||||
x="3"
|
||||
y="3"
|
||||
width="14"
|
||||
height="14"
|
||||
rx="2"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
className="plan-progress-icon"
|
||||
>
|
||||
<circle
|
||||
cx="7"
|
||||
cy="7"
|
||||
r="5.5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1"
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
className="plan-progress-icon"
|
||||
>
|
||||
<circle cx="7" cy="7" r="6" fill="currentColor" opacity="0.2" />
|
||||
<path
|
||||
d="M3 7H17M7 3V7M13 3V7"
|
||||
d="M4 7.5L6 9.5L10 4.5"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
<span className="plan-title">Task Plan</span>
|
||||
</div>
|
||||
<span className="plan-title">
|
||||
{completedCount} of {totalCount} Done
|
||||
</span>
|
||||
</div>
|
||||
<div className="plan-entries">
|
||||
{entries.map((entry, index) => (
|
||||
<div key={index} className={`plan-entry ${entry.status}`}>
|
||||
<div className="plan-entry-line"></div>
|
||||
<div className="plan-entry-icon">
|
||||
{getStatusIcon(entry.status, index)}
|
||||
</div>
|
||||
<div className="plan-entry-icon">{getStatusIcon(entry.status)}</div>
|
||||
<div className="plan-entry-content">
|
||||
<span className="plan-entry-number">{index + 1}.</span>
|
||||
<span className="plan-entry-text">{entry.content}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user