From ac9cb3a6d386e0809020850e16dc2b530aa8a051 Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Fri, 5 Dec 2025 18:03:37 +0800 Subject: [PATCH] style(vscode-ide-companion/ui): improve component styling and layout --- .../messages/Waiting/InterruptedMessage.tsx | 2 +- .../toolcalls/Think/TodoWriteToolCall.tsx | 5 +-- .../UpdatedPlan/UpdatedPlanToolCall.tsx | 5 +-- .../webview/components/ui/CheckboxDisplay.tsx | 14 +++++---- .../src/webview/styles/App.css | 20 ++++-------- .../src/webview/styles/tailwind.css | 31 ++++++++++++++++--- 6 files changed, 47 insertions(+), 30 deletions(-) diff --git a/packages/vscode-ide-companion/src/webview/components/messages/Waiting/InterruptedMessage.tsx b/packages/vscode-ide-companion/src/webview/components/messages/Waiting/InterruptedMessage.tsx index 6b574d58..fbe6ca5a 100644 --- a/packages/vscode-ide-companion/src/webview/components/messages/Waiting/InterruptedMessage.tsx +++ b/packages/vscode-ide-companion/src/webview/components/messages/Waiting/InterruptedMessage.tsx @@ -20,7 +20,7 @@ export const InterruptedMessage: React.FC = ({ style={{ width: '100%', alignItems: 'flex-start', - paddingLeft: '30px', // keep alignment with other assistant messages, but no status icon + paddingLeft: '10px', // keep alignment with other assistant messages, but no status icon position: 'relative', paddingTop: '8px', paddingBottom: '8px', diff --git a/packages/vscode-ide-companion/src/webview/components/toolcalls/Think/TodoWriteToolCall.tsx b/packages/vscode-ide-companion/src/webview/components/toolcalls/Think/TodoWriteToolCall.tsx index b74a20fc..86bd0428 100644 --- a/packages/vscode-ide-companion/src/webview/components/toolcalls/Think/TodoWriteToolCall.tsx +++ b/packages/vscode-ide-companion/src/webview/components/toolcalls/Think/TodoWriteToolCall.tsx @@ -42,7 +42,8 @@ const parseTodoEntries = (textOutputs: string[]): TodoEntry[] => { const lines = text.split(/\r?\n/); const entries: TodoEntry[] = []; - const todoRe = /^(?:\s*(?:[-*]|\d+[.)])\s*)?\[( |x|X|-)\]\s+(.*)$/; + // Accept [ ], [x]/[X] and in-progress markers [-] or [*] + const todoRe = /^(?:\s*(?:[-*]|\d+[.)])\s*)?\[( |x|X|-|\*)\]\s+(.*)$/; for (const line of lines) { const m = line.match(todoRe); if (m) { @@ -51,7 +52,7 @@ const parseTodoEntries = (textOutputs: string[]): TodoEntry[] => { const status: EntryStatus = mark === 'x' || mark === 'X' ? 'completed' - : mark === '-' + : mark === '-' || mark === '*' ? 'in_progress' : 'pending'; if (title) { diff --git a/packages/vscode-ide-companion/src/webview/components/toolcalls/UpdatedPlan/UpdatedPlanToolCall.tsx b/packages/vscode-ide-companion/src/webview/components/toolcalls/UpdatedPlan/UpdatedPlanToolCall.tsx index d08b3ab5..10a567de 100644 --- a/packages/vscode-ide-companion/src/webview/components/toolcalls/UpdatedPlan/UpdatedPlanToolCall.tsx +++ b/packages/vscode-ide-companion/src/webview/components/toolcalls/UpdatedPlan/UpdatedPlanToolCall.tsx @@ -42,7 +42,8 @@ const parsePlanEntries = (textOutputs: string[]): PlanEntry[] => { const lines = text.split(/\r?\n/); const entries: PlanEntry[] = []; - const todoRe = /^(?:\s*(?:[-*]|\d+[.)])\s*)?\[( |x|X|-)\]\s+(.*)$/; + // Accept [ ], [x]/[X] and in-progress markers [-] or [*] + const todoRe = /^(?:\s*(?:[-*]|\d+[.)])\s*)?\[( |x|X|-|\*)\]\s+(.*)$/; for (const line of lines) { const m = line.match(todoRe); if (m) { @@ -51,7 +52,7 @@ const parsePlanEntries = (textOutputs: string[]): PlanEntry[] => { const status: EntryStatus = mark === 'x' || mark === 'X' ? 'completed' - : mark === '-' + : mark === '-' || mark === '*' ? 'in_progress' : 'pending'; if (title) { diff --git a/packages/vscode-ide-companion/src/webview/components/ui/CheckboxDisplay.tsx b/packages/vscode-ide-companion/src/webview/components/ui/CheckboxDisplay.tsx index a69b7c27..243abec4 100644 --- a/packages/vscode-ide-companion/src/webview/components/ui/CheckboxDisplay.tsx +++ b/packages/vscode-ide-companion/src/webview/components/ui/CheckboxDisplay.tsx @@ -33,7 +33,7 @@ export const CheckboxDisplay: React.FC = ({ // Pseudo-elements do not reliably render on in Chromium (VS Code webviews), // which caused the missing icon. This version is font-free and uses borders. const showCheck = !!checked && !indeterminate; - const showDash = !!indeterminate; + const showInProgress = !!indeterminate; return ( = ({ ].join(' ')} /> ) : null} - {showDash ? ( + {showInProgress ? ( + > + * + ) : null} ); diff --git a/packages/vscode-ide-companion/src/webview/styles/App.css b/packages/vscode-ide-companion/src/webview/styles/App.css index 54377f5f..52f7f83c 100644 --- a/packages/vscode-ide-companion/src/webview/styles/App.css +++ b/packages/vscode-ide-companion/src/webview/styles/App.css @@ -11,7 +11,7 @@ =========================== */ :root { /* Qwen Brand Colors */ - --app-qwen-orange: #615fff; + --app-qwen-theme: #615fff; --app-qwen-clay-button-orange: #4f46e5; --app-qwen-ivory: #f5f5ff; --app-qwen-slate: #141420; @@ -46,7 +46,7 @@ --app-input-placeholder-foreground: var(--vscode-input-placeholderForeground); --app-input-secondary-background: var(--vscode-menu-background); /* Input Highlight (focus ring/border) */ - --app-input-highlight: var(--app-qwen-orange); + --app-input-highlight: var(--app-qwen-theme); /* Code Highlighting */ --app-code-background: var(--vscode-textCodeBlock-background, rgba(0, 0, 0, 0.05)); @@ -211,7 +211,7 @@ button { } .input-field:focus { - border-color: var(--app-qwen-orange); + border-color: var(--app-qwen-theme); } .input-field:disabled { @@ -355,7 +355,7 @@ button { =========================== */ .permission-request-card { background: var(--app-input-background); - border: 1px solid var(--app-qwen-orange); + border: 1px solid var(--app-qwen-theme); border-radius: var(--corner-radius-medium); margin: var(--app-spacing-medium) 0; margin-bottom: var(--app-spacing-xlarge); @@ -500,18 +500,10 @@ button { } .permission-option.selected { - border-color: var(--app-qwen-orange); + border-color: var(--app-qwen-theme); background: rgba(97, 95, 255, 0.1); } -.permission-option.allow { - /* Allow options */ -} - -.permission-option.reject { - /* Reject options */ -} - .permission-radio { flex-shrink: 0; } @@ -540,7 +532,7 @@ button { .permission-option.selected .permission-option-number { color: var(--app-qwen-ivory); - background-color: var(--app-qwen-orange); + background-color: var(--app-qwen-theme); } .permission-always-badge { diff --git a/packages/vscode-ide-companion/src/webview/styles/tailwind.css b/packages/vscode-ide-companion/src/webview/styles/tailwind.css index 9b137626..21f3c9b6 100644 --- a/packages/vscode-ide-companion/src/webview/styles/tailwind.css +++ b/packages/vscode-ide-companion/src/webview/styles/tailwind.css @@ -51,7 +51,8 @@ .composer-form:focus-within { /* match existing highlight behavior */ border-color: var(--app-input-highlight); - box-shadow: 0 1px 2px color-mix(in srgb, var(--app-input-highlight), transparent 80%); + box-shadow: 0 1px 2px + color-mix(in srgb, var(--app-input-highlight), transparent 80%); } /* Composer: input editable area */ @@ -62,7 +63,11 @@ font-size: var(--vscode-chat-font-size, 13px); color: var(--app-input-foreground); } - .composer-input:empty:before { + /* Show placeholder when truly empty OR when flagged as empty via data attribute. + The data attribute is needed because some browsers insert a
in + contentEditable, which breaks :empty matching. */ + .composer-input:empty:before, + .composer-input[data-empty='true']::before { content: attr(data-placeholder); color: var(--app-input-placeholder-foreground); pointer-events: none; @@ -76,7 +81,7 @@ outline: none; } .composer-input:disabled, - .composer-input[contenteditable="false"] { + .composer-input[contenteditable='false'] { color: #999; cursor: not-allowed; } @@ -105,7 +110,8 @@ filter: brightness(1.1); } .btn-text-compact > svg { - height: 1em; /* match font size */ + height: 1em; + width: 1em; flex-shrink: 0; } .btn-text-compact > span { @@ -119,7 +125,9 @@ } @media screen and (max-width: 300px) { - .btn-text-compact > svg { display: none; } + .btn-text-compact > svg { + display: none; + } } /* Icon-only button, compact square (26x26) */ @@ -173,3 +181,16 @@ overflow-wrap: anywhere; } } + +/* =========================== + Utilities + =========================== */ +@layer utilities { + /* Multi-line clamp with ellipsis (Chromium-based webview supported) */ + .q-line-clamp-3 { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + overflow: hidden; + } +}