feat(vscode-ide-companion): split module & notes in english

This commit is contained in:
yiliang114
2025-11-25 00:32:51 +08:00
parent 3cf22c065f
commit f503eb2520
42 changed files with 4189 additions and 3063 deletions

View File

@@ -6,6 +6,13 @@
import type React from 'react';
import type { ContextAttachment } from '../ContextAttachmentManager.js';
import {
FileListIcon,
PlusSmallIcon,
SymbolIcon,
SelectionIcon,
CloseSmallIcon,
} from './icons/index.js';
import './ContextPills.css';
interface ContextPillsProps {
@@ -28,45 +35,13 @@ export const ContextPills: React.FC<ContextPillsProps> = ({
const getIcon = (type: string) => {
switch (type) {
case 'file':
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
>
<path d="M5 3.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5Z" />
</svg>
);
return <FileListIcon />;
case 'symbol':
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
>
<path d="M8 1a.5.5 0 0 1 .5.5v5.793l2.146-2.147a.5.5 0 0 1 .708.708l-3 3a.5.5 0 0 1-.708 0l-3-3a.5.5 0 1 1 .708-.708L7.5 7.293V1.5A.5.5 0 0 1 8 1Z" />
</svg>
);
return <SymbolIcon />;
case 'selection':
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
>
<path d="M2 3.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5Zm0 4a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5Zm0 4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5Z" />
</svg>
);
return <SelectionIcon />;
default:
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
>
<path d="M8 2a.5.5 0 0 1 .5.5V5h2.5a.5.5 0 0 1 0 1H8.5v2.5a.5.5 0 0 1-1 0V6H5a.5.5 0 0 1 0-1h2.5V2.5A.5.5 0 0 1 8 2Z" />
</svg>
);
return <PlusSmallIcon />;
}
};
@@ -81,13 +56,7 @@ export const ContextPills: React.FC<ContextPillsProps> = ({
onClick={() => onRemove(attachment.id)}
aria-label="Remove attachment"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
>
<path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708Z" />
</svg>
<CloseSmallIcon />
</button>
</div>
))}

View File

@@ -5,6 +5,7 @@
*/
import type React from 'react';
import { TerminalIcon, CloseIcon } from './icons/index.js';
interface InfoBannerProps {
/**
@@ -56,22 +57,7 @@ export const InfoBanner: React.FC<InfoBannerProps> = ({
>
<div className="flex items-center flex-1 min-w-0" style={{ gap: '12px' }}>
{/* Icon */}
<svg
className="flex-shrink-0 w-4 h-4"
width="16"
height="16"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
style={{ fill: 'var(--app-primary-foreground)' }}
>
<path d="M5.14648 7.14648C5.34175 6.95122 5.65825 6.95122 5.85352 7.14648L8.35352 9.64648C8.44728 9.74025 8.5 9.86739 8.5 10C8.5 10.0994 8.47037 10.1958 8.41602 10.2773L8.35352 10.3535L5.85352 12.8535C5.65825 13.0488 5.34175 13.0488 5.14648 12.8535C4.95122 12.6583 4.95122 12.3417 5.14648 12.1465L7.29297 10L5.14648 7.85352C4.95122 7.65825 4.95122 7.34175 5.14648 7.14648Z"></path>
<path d="M14.5 12C14.7761 12 15 12.2239 15 12.5C15 12.7761 14.7761 13 14.5 13H9.5C9.22386 13 9 12.7761 9 12.5C9 12.2239 9.22386 12 9.5 12H14.5Z"></path>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M16.5 4C17.3284 4 18 4.67157 18 5.5V14.5C18 15.3284 17.3284 16 16.5 16H3.5C2.67157 16 2 15.3284 2 14.5V5.5C2 4.67157 2.67157 4 3.5 4H16.5ZM3.5 5C3.22386 5 3 5.22386 3 5.5V14.5C3 14.7761 3.22386 15 3.5 15H16.5C16.7761 15 17 14.7761 17 14.5V5.5C17 5.22386 16.7761 5 16.5 5H3.5Z"
></path>
</svg>
<TerminalIcon className="flex-shrink-0 w-4 h-4" />
{/* Message */}
<label
@@ -116,21 +102,7 @@ export const InfoBanner: React.FC<InfoBannerProps> = ({
aria-label="Close banner"
onClick={onDismiss}
>
<svg
className="w-[10px] h-[10px]"
width="10"
height="10"
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M1 1L13 13M1 13L13 1"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
></path>
</svg>
<CloseIcon className="w-[10px] h-[10px]" />
</button>
</div>
);

View File

@@ -5,6 +5,16 @@
*/
import type React from 'react';
import {
EditPencilIcon,
AutoEditIcon,
PlanModeIcon,
CodeBracketsIcon,
ThinkingIcon,
SlashCommandIcon,
LinkIcon,
ArrowUpIcon,
} from './icons/index.js';
type EditMode = 'ask' | 'auto' | 'plan';
@@ -39,50 +49,19 @@ const getEditModeInfo = (editMode: EditMode) => {
return {
text: 'Ask before edits',
title: 'Qwen will ask before each edit. Click to switch modes.',
icon: (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
aria-hidden="true"
>
<path
fillRule="evenodd"
d="M11.013 2.513a1.75 1.75 0 0 1 2.475 2.474L6.226 12.25a2.751 2.751 0 0 1-.892.596l-2.047.848a.75.75 0 0 1-.98-.98l.848-2.047a2.75 2.75 0 0 1 .596-.892l7.262-7.261Z"
clipRule="evenodd"
></path>
</svg>
),
icon: <EditPencilIcon />,
};
case 'auto':
return {
text: 'Edit automatically',
title: 'Qwen will edit files automatically. Click to switch modes.',
icon: (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
aria-hidden="true"
>
<path d="M2.53 3.956A1 1 0 0 0 1 4.804v6.392a1 1 0 0 0 1.53.848l5.113-3.196c.16-.1.279-.233.357-.383v2.73a1 1 0 0 0 1.53.849l5.113-3.196a1 1 0 0 0 0-1.696L9.53 3.956A1 1 0 0 0 8 4.804v2.731a.992.992 0 0 0-.357-.383L2.53 3.956Z"></path>
</svg>
),
icon: <AutoEditIcon />,
};
case 'plan':
return {
text: 'Plan mode',
title: 'Qwen will plan before executing. Click to switch modes.',
icon: (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
aria-hidden="true"
>
<path d="M4.5 2a.5.5 0 0 0-.5.5v11a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-11a.5.5 0 0 0-.5-.5h-1ZM10.5 2a.5.5 0 0 0-.5.5v11a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-11a.5.5 0 0 0-.5-.5h-1Z"></path>
</svg>
),
icon: <PlanModeIcon />,
};
default:
return {
@@ -208,19 +187,7 @@ export const InputForm: React.FC<InputFormProps> = ({
title={`Showing Qwen Code your current file selection: ${activeFileName}${activeSelection ? `#${activeSelection.startLine}-${activeSelection.endLine}` : ''}`}
onClick={onFocusActiveEditor}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
data-slot="icon"
>
<path
fillRule="evenodd"
d="M6.28 5.22a.75.75 0 0 1 0 1.06L2.56 10l3.72 3.72a.75.75 0 0 1-1.06 1.06L.97 10.53a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0Zm7.44 0a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L17.44 10l-3.72-3.72a.75.75 0 0 1 0-1.06ZM11.377 2.011a.75.75 0 0 1 .612.867l-2.5 14.5a.75.75 0 0 1-1.478-.255l2.5-14.5a.75.75 0 0 1 .866-.612Z"
clipRule="evenodd"
></path>
</svg>
<CodeBracketsIcon />
<span>
{activeFileName}
{activeSelection &&
@@ -259,26 +226,7 @@ export const InputForm: React.FC<InputFormProps> = ({
title={thinkingEnabled ? 'Thinking on' : 'Thinking off'}
onClick={onToggleThinking}
>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.00293 1.11523L8.35059 1.12402H8.35352C11.9915 1.30834 14.8848 4.31624 14.8848 8C14.8848 11.8025 11.8025 14.8848 8 14.8848C4.19752 14.8848 1.11523 11.8025 1.11523 8C1.11523 7.67691 1.37711 7.41504 1.7002 7.41504C2.02319 7.41514 2.28516 7.67698 2.28516 8C2.28516 11.1563 4.84369 13.7148 8 13.7148C11.1563 13.7148 13.7148 11.1563 13.7148 8C13.7148 4.94263 11.3141 2.4464 8.29492 2.29297V2.29199L7.99609 2.28516H7.9873V2.28418L7.89648 2.27539L7.88281 2.27441V2.27344C7.61596 2.21897 7.41513 1.98293 7.41504 1.7002C7.41504 1.37711 7.67691 1.11523 8 1.11523H8.00293ZM8 3.81543C8.32309 3.81543 8.58496 4.0773 8.58496 4.40039V7.6377L10.9619 8.82715C11.2505 8.97169 11.3678 9.32256 11.2236 9.61133C11.0972 9.86425 10.8117 9.98544 10.5488 9.91504L10.5352 9.91211V9.91016L10.4502 9.87891L10.4385 9.87402V9.87305L7.73828 8.52344C7.54007 8.42433 7.41504 8.22155 7.41504 8V4.40039C7.41504 4.0773 7.67691 3.81543 8 3.81543ZM2.44336 5.12695C2.77573 5.19517 3.02597 5.48929 3.02637 5.8418C3.02637 6.19456 2.7761 6.49022 2.44336 6.55859L2.2959 6.57324C1.89241 6.57324 1.56543 6.24529 1.56543 5.8418C1.56588 5.43853 1.89284 5.1123 2.2959 5.1123L2.44336 5.12695ZM3.46094 2.72949C3.86418 2.72984 4.19017 3.05712 4.19043 3.45996V3.46094C4.19009 3.86393 3.86392 4.19008 3.46094 4.19043H3.45996C3.05712 4.19017 2.72983 3.86419 2.72949 3.46094V3.45996C2.72976 3.05686 3.05686 2.72976 3.45996 2.72949H3.46094ZM5.98926 1.58008C6.32235 1.64818 6.57324 1.94276 6.57324 2.2959L6.55859 2.44336C6.49022 2.7761 6.19456 3.02637 5.8418 3.02637C5.43884 3.02591 5.11251 2.69895 5.1123 2.2959L5.12695 2.14844C5.19504 1.81591 5.48906 1.56583 5.8418 1.56543L5.98926 1.58008Z"
strokeWidth="0.27"
style={{
stroke: thinkingEnabled
? 'var(--app-qwen-ivory)'
: 'var(--app-secondary-foreground)',
fill: thinkingEnabled
? 'var(--app-qwen-ivory)'
: 'var(--app-secondary-foreground)',
}}
></path>
</svg>
<ThinkingIcon enabled={thinkingEnabled} />
</button>
{/* Command button */}
@@ -289,18 +237,7 @@ export const InputForm: React.FC<InputFormProps> = ({
title="Show command menu (/)"
onClick={onShowCommandMenu}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fillRule="evenodd"
d="M12.528 3.047a.75.75 0 0 1 .449.961L8.433 16.504a.75.75 0 1 1-1.41-.512l4.544-12.496a.75.75 0 0 1 .961-.449Z"
clipRule="evenodd"
></path>
</svg>
<SlashCommandIcon />
</button>
{/* Attach button */}
@@ -311,18 +248,7 @@ export const InputForm: React.FC<InputFormProps> = ({
title="Attach context (Cmd/Ctrl + /)"
onClick={onAttachContext}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fillRule="evenodd"
d="M15.621 4.379a3 3 0 0 0-4.242 0l-7 7a3 3 0 0 0 4.241 4.243h.001l.497-.5a.75.75 0 0 1 1.064 1.057l-.498.501-.002.002a4.5 4.5 0 0 1-6.364-6.364l7-7a4.5 4.5 0 0 1 6.368 6.36l-3.455 3.553A2.625 2.625 0 1 1 9.52 9.52l3.45-3.451a.75.75 0 1 1 1.061 1.06l-3.45 3.451a1.125 1.125 0 0 0 1.587 1.595l3.454-3.553a3 3 0 0 0 0-4.242Z"
clipRule="evenodd"
></path>
</svg>
<LinkIcon />
</button>
{/* Send button */}
@@ -335,18 +261,7 @@ export const InputForm: React.FC<InputFormProps> = ({
}}
disabled={isStreaming || !inputText.trim()}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fillRule="evenodd"
d="M10 17a.75.75 0 0 1-.75-.75V5.612L5.29 9.77a.75.75 0 0 1-1.08-1.04l5.25-5.5a.75.75 0 0 1 1.08 0l5.25 5.5a.75.75 0 1 1-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0 1 10 17Z"
clipRule="evenodd"
></path>
</svg>
<ArrowUpIcon />
</button>
</div>
</form>

View File

@@ -5,6 +5,7 @@
*/
import type React from 'react';
import { WarningTriangleIcon, CloseIcon } from './icons/index.js';
interface NotLoggedInMessageProps {
/**
@@ -28,99 +29,76 @@ export const NotLoggedInMessage: React.FC<NotLoggedInMessageProps> = ({
onLoginClick,
onDismiss,
}) => (
<div
className="flex items-start gap-3 p-4 my-4 rounded-lg"
style={{
backgroundColor: 'var(--app-warning-background)',
borderLeft: '3px solid var(--app-warning-border)',
}}
>
{/* Warning Icon */}
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
className="flex-shrink-0 w-5 h-5 mt-0.5"
style={{ color: 'var(--app-warning-foreground)' }}
<div
className="flex items-start gap-3 p-4 my-4 rounded-lg"
style={{
backgroundColor: 'var(--app-warning-background)',
borderLeft: '3px solid var(--app-warning-border)',
}}
>
{/* Warning Icon */}
<WarningTriangleIcon
className="flex-shrink-0 w-5 h-5 mt-0.5"
style={{ color: 'var(--app-warning-foreground)' }}
/>
{/* Content */}
<div className="flex-1 min-w-0">
<p
className="m-0 mb-3 text-sm leading-relaxed"
style={{ color: 'var(--app-primary-foreground)' }}
>
<path
fillRule="evenodd"
d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495ZM10 5a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 10 5Zm0 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
clipRule="evenodd"
/>
</svg>
{message}
</p>
{/* Content */}
<div className="flex-1 min-w-0">
<p
className="m-0 mb-3 text-sm leading-relaxed"
style={{ color: 'var(--app-primary-foreground)' }}
>
{message}
</p>
{/* Login Button */}
<button
className="px-4 py-2 text-sm font-medium rounded transition-colors duration-200"
style={{
backgroundColor: 'var(--app-qwen-orange)',
color: 'white',
border: 'none',
}}
onMouseEnter={(e) => {
e.currentTarget.style.opacity = '0.9';
}}
onMouseLeave={(e) => {
e.currentTarget.style.opacity = '1';
}}
onClick={() => {
if (onDismiss) {
onDismiss();
}
onLoginClick();
}}
>
Login Now
</button>
</div>
{/* Optional Close Button */}
{onDismiss && (
<button
className="flex-shrink-0 flex items-center justify-center cursor-pointer rounded"
style={{
background: 'none',
border: 'none',
padding: '6px',
color: 'var(--app-secondary-foreground)',
borderRadius: '4px',
}}
onMouseEnter={(e) => {
e.currentTarget.style.backgroundColor =
'var(--app-ghost-button-hover-background)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = 'transparent';
}}
aria-label="Dismiss"
onClick={onDismiss}
>
<svg
className="w-[10px] h-[10px]"
width="10"
height="10"
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M1 1L13 13M1 13L13 1"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
</button>
)}
{/* Login Button */}
<button
className="px-4 py-2 text-sm font-medium rounded transition-colors duration-200"
style={{
backgroundColor: 'var(--app-qwen-orange)',
color: 'white',
border: 'none',
}}
onMouseEnter={(e) => {
e.currentTarget.style.opacity = '0.9';
}}
onMouseLeave={(e) => {
e.currentTarget.style.opacity = '1';
}}
onClick={() => {
if (onDismiss) {
onDismiss();
}
onLoginClick();
}}
>
Login Now
</button>
</div>
);
{/* Optional Close Button */}
{onDismiss && (
<button
className="flex-shrink-0 flex items-center justify-center cursor-pointer rounded"
style={{
background: 'none',
border: 'none',
padding: '6px',
color: 'var(--app-secondary-foreground)',
borderRadius: '4px',
}}
onMouseEnter={(e) => {
e.currentTarget.style.backgroundColor =
'var(--app-ghost-button-hover-background)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = 'transparent';
}}
aria-label="Dismiss"
onClick={onDismiss}
>
<CloseIcon className="w-[10px] h-[10px]" />
</button>
)}
</div>
);

View File

@@ -5,6 +5,11 @@
*/
import type React from 'react';
import {
PlanCompletedIcon,
PlanInProgressIcon,
PlanPendingIcon,
} from './icons/index.js';
import './PlanDisplay.css';
export interface PlanEntry {
@@ -28,63 +33,12 @@ export const PlanDisplay: React.FC<PlanDisplayProps> = ({ entries }) => {
const getStatusIcon = (status: string) => {
switch (status) {
case 'completed':
return (
<svg
width="14"
height="14"
viewBox="0 0 14 14"
fill="none"
className="plan-icon completed"
>
<circle cx="7" cy="7" r="6" fill="currentColor" opacity="0.2" />
<path
d="M4 7.5L6 9.5L10 4.5"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
return <PlanCompletedIcon className="plan-icon completed" />;
case 'in_progress':
return (
<svg
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>
);
return <PlanInProgressIcon className="plan-icon in-progress" />;
default:
// pending
return (
<svg
width="14"
height="14"
viewBox="0 0 14 14"
fill="none"
className="plan-icon pending"
>
<circle
cx="7"
cy="7"
r="5.5"
fill="none"
stroke="currentColor"
strokeWidth="1"
/>
</svg>
);
return <PlanPendingIcon className="plan-icon pending" />;
}
};
@@ -92,38 +46,8 @@ export const PlanDisplay: React.FC<PlanDisplayProps> = ({ entries }) => {
<div className="plan-display">
<div className="plan-header">
<div className="plan-progress-icons">
<svg
width="14"
height="14"
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="M4 7.5L6 9.5L10 4.5"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
<PlanPendingIcon className="plan-progress-icon" />
<PlanCompletedIcon className="plan-progress-icon" />
</div>
<span className="plan-title">
{completedCount} of {totalCount} Done

View File

@@ -6,6 +6,7 @@
import type React from 'react';
import { useState, useEffect, useRef } from 'react';
import { CloseIcon } from './icons/index.js';
interface SaveSessionDialogProps {
isOpen: boolean;
@@ -70,14 +71,7 @@ export const SaveSessionDialog: React.FC<SaveSessionDialogProps> = ({
<div className="dialog-header">
<h3>Save Conversation</h3>
<button className="dialog-close" onClick={onClose} aria-label="Close">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path
d="M12 4L4 12M4 4L12 12"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
<CloseIcon width="16" height="16" />
</button>
</div>

View File

@@ -6,6 +6,13 @@
import React, { useState, useEffect } from 'react';
import { useVSCode } from '../hooks/useVSCode.js';
import {
RefreshIcon,
SaveDocumentIcon,
SearchIcon,
PlayIcon,
SwitchIcon,
} from './icons/index.js';
interface Session {
id: string;
@@ -86,63 +93,19 @@ export const SessionManager: React.FC<SessionManagerProps> = ({
disabled={isLoading}
title="Refresh sessions"
>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path
d="M13.3333 8C13.3333 10.9455 10.9455 13.3333 8 13.3333C5.05451 13.3333 2.66663 10.9455 2.66663 8C2.66663 5.05451 5.05451 2.66663 8 2.66663"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
<path
d="M10.6666 8L13.3333 8M13.3333 8L13.3333 5.33333M13.3333 8L10.6666 10.6667"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
<RefreshIcon width="16" height="16" />
</button>
</div>
<div className="session-manager-actions">
<button className="secondary-button" onClick={handleSaveCurrent}>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path
d="M2.66663 2.66663H10.6666L13.3333 5.33329V13.3333H2.66663V2.66663Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M8 10.6666V8M8 8V5.33329M8 8H10.6666M8 8H5.33329"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
<SaveDocumentIcon width="16" height="16" />
Save Current
</button>
</div>
<div className="session-search">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path
d="M7.33329 12.6666C10.2788 12.6666 12.6666 10.2788 12.6666 7.33329C12.6666 4.38777 10.2788 2 7.33329 2C4.38777 2 2 4.38777 2 7.33329C2 10.2788 4.38777 12.6666 7.33329 12.6666Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M13.9999 14L11.0999 11.1"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
<SearchIcon width="16" height="16" />
<input
type="text"
placeholder="Search conversations..."
@@ -187,36 +150,14 @@ export const SessionManager: React.FC<SessionManagerProps> = ({
onClick={() => handleResumeSession(session.id)}
title="Resume this conversation"
>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path
d="M5.33337 4L10.6667 8L5.33337 12"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
<PlayIcon width="16" height="16" />
</button>
<button
className="icon-button"
onClick={() => handleSwitchSession(session.id)}
title="Switch to this conversation"
>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path
d="M10.6666 4L13.3333 6.66667L10.6666 9.33333"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M2.66663 6.66667H13.3333"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
<SwitchIcon width="16" height="16" />
</button>
</div>
</div>

View File

@@ -0,0 +1,65 @@
/**
* @license
* Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*
* Playback and session control icons
*/
import type React from 'react';
import type { IconProps } from './types.js';
/**
* Play/resume icon (16x16)
* Used for resume session
*/
export const PlayIcon: React.FC<IconProps> = ({
size = 16,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path d="M5.33337 4L10.6667 8L5.33337 12" />
</svg>
);
/**
* Switch/arrow right icon (16x16)
* Used for switch session
*/
export const SwitchIcon: React.FC<IconProps> = ({
size = 16,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path d="M10.6666 4L13.3333 6.66667L10.6666 9.33333" />
<path d="M2.66663 6.66667H13.3333" />
</svg>
);

View File

@@ -0,0 +1,187 @@
/**
* @license
* Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*
* Edit mode related icons
*/
import type React from 'react';
import type { IconProps } from './types.js';
/**
* Edit pencil icon (16x16)
* Used for "Ask before edits" mode
*/
export const EditPencilIcon: React.FC<IconProps> = ({
size = 16,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path
fillRule="evenodd"
d="M11.013 2.513a1.75 1.75 0 0 1 2.475 2.474L6.226 12.25a2.751 2.751 0 0 1-.892.596l-2.047.848a.75.75 0 0 1-.98-.98l.848-2.047a2.75 2.75 0 0 1 .596-.892l7.262-7.261Z"
clipRule="evenodd"
/>
</svg>
);
/**
* Auto/fast-forward icon (16x16)
* Used for "Edit automatically" mode
*/
export const AutoEditIcon: React.FC<IconProps> = ({
size = 16,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path d="M2.53 3.956A1 1 0 0 0 1 4.804v6.392a1 1 0 0 0 1.53.848l5.113-3.196c.16-.1.279-.233.357-.383v2.73a1 1 0 0 0 1.53.849l5.113-3.196a1 1 0 0 0 0-1.696L9.53 3.956A1 1 0 0 0 8 4.804v2.731a.992.992 0 0 0-.357-.383L2.53 3.956Z" />
</svg>
);
/**
* Plan mode/bars icon (16x16)
* Used for "Plan mode"
*/
export const PlanModeIcon: React.FC<IconProps> = ({
size = 16,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path d="M4.5 2a.5.5 0 0 0-.5.5v11a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-11a.5.5 0 0 0-.5-.5h-1ZM10.5 2a.5.5 0 0 0-.5.5v11a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-11a.5.5 0 0 0-.5-.5h-1Z" />
</svg>
);
/**
* Code brackets icon (20x20)
* Used for active file indicator
*/
export const CodeBracketsIcon: React.FC<IconProps> = ({
size = 20,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path
fillRule="evenodd"
d="M6.28 5.22a.75.75 0 0 1 0 1.06L2.56 10l3.72 3.72a.75.75 0 0 1-1.06 1.06L.97 10.53a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0Zm7.44 0a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L17.44 10l-3.72-3.72a.75.75 0 0 1 0-1.06ZM11.377 2.011a.75.75 0 0 1 .612.867l-2.5 14.5a.75.75 0 0 1-1.478-.255l2.5-14.5a.75.75 0 0 1 .866-.612Z"
clipRule="evenodd"
/>
</svg>
);
/**
* Slash command icon (20x20)
* Used for command menu button
*/
export const SlashCommandIcon: React.FC<IconProps> = ({
size = 20,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path
fillRule="evenodd"
d="M12.528 3.047a.75.75 0 0 1 .449.961L8.433 16.504a.75.75 0 1 1-1.41-.512l4.544-12.496a.75.75 0 0 1 .961-.449Z"
clipRule="evenodd"
/>
</svg>
);
/**
* Link/attachment icon (20x20)
* Used for attach context button
*/
export const LinkIcon: React.FC<IconProps> = ({
size = 20,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path
fillRule="evenodd"
d="M15.621 4.379a3 3 0 0 0-4.242 0l-7 7a3 3 0 0 0 4.241 4.243h.001l.497-.5a.75.75 0 0 1 1.064 1.057l-.498.501-.002.002a4.5 4.5 0 0 1-6.364-6.364l7-7a4.5 4.5 0 0 1 6.368 6.36l-3.455 3.553A2.625 2.625 0 1 1 9.52 9.52l3.45-3.451a.75.75 0 1 1 1.061 1.06l-3.45 3.451a1.125 1.125 0 0 0 1.587 1.595l3.454-3.553a3 3 0 0 0 0-4.242Z"
clipRule="evenodd"
/>
</svg>
);
/**
* Open diff icon (16x16)
* Used for opening diff in VS Code
*/
export const OpenDiffIcon: React.FC<IconProps> = ({
size = 16,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path d="M13.5 7l-4-4v3h-6v2h6v3l4-4z" />
</svg>
);

View File

@@ -0,0 +1,84 @@
/**
* @license
* Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*
* File and document related icons
*/
import type React from 'react';
import type { IconProps } from './types.js';
/**
* File document icon (16x16)
* Used for file completion menu
*/
export const FileIcon: React.FC<IconProps> = ({
size = 16,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path d="M9 2H4a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7l-5-5zm3 7V3.5L10.5 2H10v3a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V2H4a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1zM6 3h3v2H6V3z" />
</svg>
);
/**
* File list icon (16x16)
* Used for file type indicator in context pills
*/
export const FileListIcon: React.FC<IconProps> = ({
size = 16,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path d="M5 3.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5Z" />
</svg>
);
/**
* Save document icon (16x16)
* Used for save session button
*/
export const SaveDocumentIcon: React.FC<IconProps> = ({
size = 16,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path d="M2.66663 2.66663H10.6666L13.3333 5.33329V13.3333H2.66663V2.66663Z" />
<path d="M8 10.6666V8M8 8V5.33329M8 8H10.6666M8 8H5.33329" />
</svg>
);

View File

@@ -0,0 +1,216 @@
/**
* @license
* Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*
* Navigation and action icons
*/
import type React from 'react';
import type { IconProps } from './types.js';
/**
* Chevron down icon (20x20)
* Used for dropdown arrows
*/
export const ChevronDownIcon: React.FC<IconProps> = ({
size = 20,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path
fillRule="evenodd"
d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z"
clipRule="evenodd"
/>
</svg>
);
/**
* Plus icon (20x20)
* Used for new session button
*/
export const PlusIcon: React.FC<IconProps> = ({
size = 20,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path d="M10.75 4.75a.75.75 0 0 0-1.5 0v4.5h-4.5a.75.75 0 0 0 0 1.5h4.5v4.5a.75.75 0 0 0 1.5 0v-4.5h4.5a.75.75 0 0 0 0-1.5h-4.5v-4.5Z" />
</svg>
);
/**
* Small plus icon (16x16)
* Used for default attachment type
*/
export const PlusSmallIcon: React.FC<IconProps> = ({
size = 16,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path d="M8 2a.5.5 0 0 1 .5.5V5h2.5a.5.5 0 0 1 0 1H8.5v2.5a.5.5 0 0 1-1 0V6H5a.5.5 0 0 1 0-1h2.5V2.5A.5.5 0 0 1 8 2Z" />
</svg>
);
/**
* Arrow up icon (20x20)
* Used for send message button
*/
export const ArrowUpIcon: React.FC<IconProps> = ({
size = 20,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path
fillRule="evenodd"
d="M10 17a.75.75 0 0 1-.75-.75V5.612L5.29 9.77a.75.75 0 0 1-1.08-1.04l5.25-5.5a.75.75 0 0 1 1.08 0l5.25 5.5a.75.75 0 1 1-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0 1 10 17Z"
clipRule="evenodd"
/>
</svg>
);
/**
* Close X icon (14x14)
* Used for close buttons in banners and dialogs
*/
export const CloseIcon: React.FC<IconProps> = ({
size = 14,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 14 14"
fill="none"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path
d="M1 1L13 13M1 13L13 1"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
);
/**
* Close X icon for context pills (16x16)
* Used to remove attachments
*/
export const CloseSmallIcon: React.FC<IconProps> = ({
size = 16,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708Z" />
</svg>
);
/**
* Search/magnifying glass icon (20x20)
* Used for search input
*/
export const SearchIcon: React.FC<IconProps> = ({
size = 20,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path
fillRule="evenodd"
d="M9 3.5a5.5 5.5 0 1 0 0 11 5.5 5.5 0 0 0 0-11ZM2 9a7 7 0 1 1 12.452 4.391l3.328 3.329a.75.75 0 1 1-1.06 1.06l-3.329-3.328A7 7 0 0 1 2 9Z"
clipRule="evenodd"
/>
</svg>
);
/**
* Refresh/reload icon (16x16)
* Used for refresh session list
*/
export const RefreshIcon: React.FC<IconProps> = ({
size = 16,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path d="M13.3333 8C13.3333 10.9455 10.9455 13.3333 8 13.3333C5.05451 13.3333 2.66663 10.9455 2.66663 8C2.66663 5.05451 5.05451 2.66663 8 2.66663" />
<path d="M10.6666 8L13.3333 8M13.3333 8L13.3333 5.33333M13.3333 8L10.6666 10.6667" />
</svg>
);

View File

@@ -0,0 +1,87 @@
/**
* @license
* Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*
* Special UI icons
*/
import type React from 'react';
import type { IconProps } from './types.js';
interface ThinkingIconProps extends IconProps {
/**
* Whether thinking is enabled (affects styling)
*/
enabled?: boolean;
}
/**
* Thinking/brain wave icon (16x16)
* Used for thinking mode toggle
*/
export const ThinkingIcon: React.FC<ThinkingIconProps> = ({
size = 16,
className,
enabled = false,
style,
...props
}) => (
<svg
width={size}
height={size}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
aria-hidden="true"
{...props}
>
<path
d="M10.3927 6.81824C10.3927 7.14824 10.1767 7.42424 9.91668 7.42424H9.75968L9.30068 5.39324C9.47868 5.28524 9.71068 5.22424 9.91668 5.22424C10.1607 5.22424 10.3927 5.50824 10.3927 5.83624V6.81824ZM4.90268 5.19124C4.55468 5.14224 4.22568 5.42624 4.22568 5.83624V6.81824C4.22568 7.17324 4.51068 7.42424 4.80068 7.42424H4.90268V5.19124ZM4.90268 8.62224C4.90268 8.70424 4.89068 8.77724 4.86768 8.84524L4.72268 9.34024C4.64868 9.58424 4.45768 9.77224 4.22568 9.84624V8.62224C4.22568 8.25824 4.51068 8.01824 4.80068 8.01824H4.90268V8.62224ZM4.78068 10.4622C4.71468 10.6532 4.60968 10.8202 4.45768 10.9572L4.05568 11.3252C3.63768 11.7132 3.00268 11.6392 2.66968 11.1692C2.35768 10.7292 2.45668 10.1212 2.89268 9.79624L3.16868 9.59424C3.36768 9.44724 3.64068 9.51324 3.76268 9.74424L4.14268 10.4622H4.78068ZM5.50268 8.62224C5.50268 8.89724 5.70768 9.08624 5.96068 9.10124H6.15568V10.9802C6.15568 11.3402 5.88268 11.6132 5.56068 11.6132H5.02068C4.68868 11.6132 4.42668 11.3302 4.42668 10.9802V9.85824C4.42668 9.72024 4.55568 9.65324 4.68568 9.70524C4.93568 9.80324 5.17568 9.77924 5.36468 9.72324C5.43568 9.70224 5.50268 9.63324 5.50268 9.54824V8.62224ZM5.50268 7.42424H5.80868H6.07968V8.01824H5.80868C5.62068 8.01824 5.50268 8.25824 5.50268 8.62224V7.42424ZM6.72668 4.93024C6.60368 4.93024 6.49468 4.98624 6.42568 5.08124L6.07968 5.55924V4.79224C6.07968 4.60024 6.14768 4.41824 6.25568 4.26724C6.36368 4.11624 6.53468 4.00724 6.72668 4.00724H8.19668C8.41568 4.00724 8.60068 4.14024 8.69768 4.32424L9.08968 5.07524H9.91668C10.5367 5.07524 10.9927 5.52224 10.9927 6.08724V6.56924C10.9927 7.13824 10.5367 7.57724 9.91668 7.57724H9.60568L9.88968 8.80224C9.95068 9.06924 9.78268 9.33324 9.50668 9.37124C9.46268 9.37724 9.41668 9.37724 9.37168 9.37124L9.23368 9.34524L8.72668 11.2652C8.61668 11.6692 8.24568 11.9232 7.80868 11.9232C7.59668 11.9232 7.39468 11.8462 7.24068 11.7082L7.14068 11.6132H7.69168V10.0562H7.07168C6.79468 10.0562 6.56968 9.81524 6.56968 9.51424V9.10024L6.78468 8.77724C6.84768 8.68324 6.87968 8.57824 6.87968 8.46824C6.87968 8.24324 6.75268 8.01824 6.55768 8.01824H6.42668V5.89024L7.27568 5.18624C7.47468 5.02524 7.56068 4.98924 7.56068 4.93024C7.56068 4.86724 7.40568 4.78724 7.20668 4.85024L6.72668 5.00524V4.93024ZM8.38268 8.01824H8.73068L9.01568 9.24624L8.30868 9.09724L8.38268 8.72824V8.01824ZM8.11668 10.0552H8.00968V11.1032L8.26668 10.1322L8.11668 10.0552ZM9.07368 10.2272L8.52468 11.7662C8.47068 11.9092 8.34668 12.0082 8.20168 12.0362C8.14268 12.0482 8.08168 12.0482 8.02068 12.0362C7.86568 12.0062 7.73068 11.9042 7.66968 11.7552C7.61268 11.6142 7.58268 11.5622 7.55268 11.4912L7.41168 11.1662L7.36468 10.7362C7.35668 10.6562 7.40068 10.5822 7.47368 10.5482C7.56368 10.5082 7.61568 10.4402 7.64068 10.3502L7.69168 10.0562H8.00968L8.68268 10.4422L9.07368 10.2272ZM7.69168 7.27024V5.86524L7.62968 5.91624C7.43768 6.07324 7.15468 6.08424 6.95168 5.94724L6.87968 5.89724V7.42424H8.21368L8.00968 6.53424C7.96768 6.35324 8.06568 6.17024 8.22568 6.10524C8.27368 6.08724 8.32168 6.08524 8.37068 6.09524L9.02868 6.22224C9.15768 6.24724 9.22668 6.39524 9.17268 6.52424L9.09668 6.71024C9.08368 6.74324 9.06368 6.77124 9.04068 6.79624L8.21268 7.57724L7.95468 7.27024H7.69168ZM8.00968 9.26224C7.94168 9.30024 7.88368 9.34024 7.83068 9.38624C7.74368 9.46024 7.66768 9.54724 7.60968 9.65224L7.54668 9.76324C7.50868 9.83224 7.48468 9.90924 7.47468 9.99124L7.47268 10.0032V10.0562H6.72668V9.51124C6.72668 9.41924 6.77068 9.33424 6.84668 9.28024L7.05768 9.13024C7.12568 9.08224 7.16268 9.00424 7.16268 8.92024V8.01824H7.83068V8.77724H8.07368V8.81624C8.07368 8.94124 8.05168 9.06024 8.00968 9.17324V9.26224ZM6.72668 8.57324C6.72668 8.57024 6.72668 8.56524 6.72668 8.56224V8.01824H6.87968V8.46824C6.87968 8.50624 6.87568 8.54324 6.86868 8.57924L6.72668 8.57324Z"
strokeWidth="0.27"
style={{
stroke: enabled
? 'var(--app-qwen-ivory)'
: 'var(--app-secondary-foreground)',
fill: enabled
? 'var(--app-qwen-ivory)'
: 'var(--app-secondary-foreground)',
...style,
}}
/>
</svg>
);
/**
* Terminal/code editor icon (20x20)
* Used for terminal preference info banner
*/
export const TerminalIcon: React.FC<IconProps> = ({
size = 20,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path
fillRule="evenodd"
d="M5.14648 7.14648C5.34175 6.95122 5.65825 6.95122 5.85352 7.14648L8.35352 9.64648C8.44728 9.74025 8.5 9.86739 8.5 10C8.5 10.0994 8.47037 10.1958 8.41602 10.2773L8.35352 10.3535L5.85352 12.8535C5.65825 13.0488 5.34175 13.0488 5.14648 12.8535C4.95122 12.6583 4.95122 12.3417 5.14648 12.1465L7.29297 10L5.14648 7.85352C4.95122 7.65825 4.95122 7.34175 5.14648 7.14648Z"
clipRule="evenodd"
/>
<path d="M14.5 12C14.7761 12 15 12.2239 15 12.5C15 12.7761 14.7761 13 14.5 13H9.5C9.22386 13 9 12.7761 9 12.5C9 12.2239 9.22386 12 9.5 12H14.5Z" />
<path
fillRule="evenodd"
d="M16.5 4C17.3284 4 18 4.67157 18 5.5V14.5C18 15.3284 17.3284 16 16.5 16H3.5C2.67157 16 2 15.3284 2 14.5V5.5C2 4.67157 2.67157 4 3.5 4H16.5ZM3.5 5C3.22386 5 3 5.22386 3 5.5V14.5C3 14.7761 3.22386 15 3.5 15H16.5C16.7761 15 17 14.7761 17 14.5V5.5C17 5.22386 16.7761 5 16.5 5H3.5Z"
clipRule="evenodd"
/>
</svg>
);

View File

@@ -0,0 +1,196 @@
/**
* @license
* Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*
* Status and state related icons
*/
import type React from 'react';
import type { IconProps } from './types.js';
/**
* Plan completed icon (14x14)
* Used for completed plan items
*/
export const PlanCompletedIcon: React.FC<IconProps> = ({
size = 14,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 14 14"
fill="none"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<circle cx="7" cy="7" r="6" fill="currentColor" opacity="0.2" />
<path
d="M4 7.5L6 9.5L10 4.5"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
/**
* Plan in progress icon (14x14)
* Used for in-progress plan items
*/
export const PlanInProgressIcon: React.FC<IconProps> = ({
size = 14,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 14 14"
fill="none"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<circle
cx="7"
cy="7"
r="5"
fill="none"
stroke="currentColor"
strokeWidth="2.5"
/>
</svg>
);
/**
* Plan pending icon (14x14)
* Used for pending plan items
*/
export const PlanPendingIcon: React.FC<IconProps> = ({
size = 14,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 14 14"
fill="none"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<circle
cx="7"
cy="7"
r="5.5"
fill="none"
stroke="currentColor"
strokeWidth="1"
/>
</svg>
);
/**
* Warning triangle icon (20x20)
* Used for warning messages
*/
export const WarningTriangleIcon: React.FC<IconProps> = ({
size = 20,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path
fillRule="evenodd"
d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495ZM10 5a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 10 5Zm0 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
clipRule="evenodd"
/>
</svg>
);
/**
* User profile icon (16x16)
* Used for login command
*/
export const UserIcon: React.FC<IconProps> = ({
size = 16,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM12.735 14c.618 0 1.093-.561.872-1.139a6.002 6.002 0 0 0-11.215 0c-.22.578.254 1.139.872 1.139h9.47Z" />
</svg>
);
/**
* Symbol arrow icon (16x16)
* Used for symbol type in context pills
*/
export const SymbolIcon: React.FC<IconProps> = ({
size = 16,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path d="M8 1a.5.5 0 0 1 .5.5v5.793l2.146-2.147a.5.5 0 0 1 .708.708l-3 3a.5.5 0 0 1-.708 0l-3-3a.5.5 0 1 1 .708-.708L7.5 7.293V1.5A.5.5 0 0 1 8 1Z" />
</svg>
);
/**
* Selection/text lines icon (16x16)
* Used for selection type in context pills
*/
export const SelectionIcon: React.FC<IconProps> = ({
size = 16,
className,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
width={size}
height={size}
className={className}
aria-hidden="true"
{...props}
>
<path d="M2 3.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5Zm0 4a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5Zm0 4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5Z" />
</svg>
);

View File

@@ -0,0 +1,53 @@
/**
* @license
* Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*
* Icons index - exports all icon components
*/
// Types
export type { IconProps } from './types.js';
// File icons
export { FileIcon, FileListIcon, SaveDocumentIcon } from './FileIcons.js';
// Navigation icons
export {
ChevronDownIcon,
PlusIcon,
PlusSmallIcon,
ArrowUpIcon,
CloseIcon,
CloseSmallIcon,
SearchIcon,
RefreshIcon,
} from './NavigationIcons.js';
// Edit mode icons
export {
EditPencilIcon,
AutoEditIcon,
PlanModeIcon,
CodeBracketsIcon,
SlashCommandIcon,
LinkIcon,
OpenDiffIcon,
} from './EditIcons.js';
// Status icons
export {
PlanCompletedIcon,
PlanInProgressIcon,
PlanPendingIcon,
WarningTriangleIcon,
UserIcon,
SymbolIcon,
SelectionIcon,
} from './StatusIcons.js';
// Action icons
export { PlayIcon, SwitchIcon } from './ActionIcons.js';
// Special icons
export { ThinkingIcon, TerminalIcon } from './SpecialIcons.js';

View File

@@ -0,0 +1,22 @@
/**
* @license
* Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*
* Common icon props interface
*/
import type React from 'react';
export interface IconProps extends React.SVGProps<SVGSVGElement> {
/**
* Icon size (width and height)
* @default 16
*/
size?: number;
/**
* Additional CSS classes
*/
className?: string;
}

View File

@@ -5,6 +5,7 @@
*/
import type React from 'react';
import { ChevronDownIcon, PlusIcon } from '../icons/index.js';
interface ChatHeaderProps {
currentSessionTitle: string;
@@ -41,19 +42,7 @@ export const ChatHeader: React.FC<ChatHeaderProps> = ({
<span style={{ fontSize: 'var(--vscode-chat-font-size, 13px)' }}>
{currentSessionTitle}
</span>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
className="w-3.5 h-3.5"
>
<path
fillRule="evenodd"
d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z"
clipRule="evenodd"
></path>
</svg>
<ChevronDownIcon className="w-3.5 h-3.5" />
</span>
</button>
@@ -94,16 +83,7 @@ export const ChatHeader: React.FC<ChatHeaderProps> = ({
onClick={onNewSession}
title="New Session"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
data-slot="icon"
className="w-4 h-4"
>
<path d="M10.75 4.75a.75.75 0 0 0-1.5 0v4.5h-4.5a.75.75 0 0 0 0 1.5h4.5v4.5a.75.75 0 0 0 1.5 0v-4.5h4.5a.75.75 0 0 0 0-1.5h-4.5v-4.5Z"></path>
</svg>
<PlusIcon className="w-4 h-4" />
</button>
</div>
);

View File

@@ -0,0 +1,121 @@
/**
* @license
* Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
import React from 'react';
import { groupSessionsByDate } from '../../utils/sessionGrouping.js';
import { getTimeAgo } from '../../utils/timeUtils.js';
import { SearchIcon } from '../icons/index.js';
interface SessionSelectorProps {
visible: boolean;
sessions: Array<Record<string, unknown>>;
currentSessionId: string | null;
searchQuery: string;
onSearchChange: (query: string) => void;
onSelectSession: (sessionId: string) => void;
onClose: () => void;
}
/**
* 会话选择器组件
* 显示会话列表并支持搜索和选择
*/
export const SessionSelector: React.FC<SessionSelectorProps> = ({
visible,
sessions,
currentSessionId,
searchQuery,
onSearchChange,
onSelectSession,
onClose,
}) => {
if (!visible) {
return null;
}
const hasNoSessions = sessions.length === 0;
return (
<>
<div className="session-selector-backdrop" onClick={onClose} />
<div
className="session-dropdown"
tabIndex={-1}
style={{
top: '34px',
left: '10px',
}}
onClick={(e) => e.stopPropagation()}
>
{/* Search Box */}
<div className="session-search">
<SearchIcon className="session-search-icon" />
<input
type="text"
className="session-search-input"
placeholder="Search sessions…"
value={searchQuery}
onChange={(e) => onSearchChange(e.target.value)}
/>
</div>
{/* Session List with Grouping */}
<div className="session-list-content">
{hasNoSessions ? (
<div
style={{
padding: '20px',
textAlign: 'center',
color: 'var(--app-secondary-foreground)',
}}
>
{searchQuery ? 'No matching sessions' : 'No sessions available'}
</div>
) : (
groupSessionsByDate(sessions).map((group) => (
<React.Fragment key={group.label}>
<div className="session-group-label">{group.label}</div>
<div className="session-group">
{group.sessions.map((session) => {
const sessionId =
(session.id as string) ||
(session.sessionId as string) ||
'';
const title =
(session.title as string) ||
(session.name as string) ||
'Untitled';
const lastUpdated =
(session.lastUpdated as string) ||
(session.startTime as string) ||
'';
const isActive = sessionId === currentSessionId;
return (
<button
key={sessionId}
className={`session-item ${isActive ? 'active' : ''}`}
onClick={() => {
onSelectSession(sessionId);
onClose();
}}
>
<span className="session-item-title">{title}</span>
<span className="session-item-time">
{getTimeAgo(lastUpdated)}
</span>
</button>
);
})}
</div>
</React.Fragment>
))
)}
</div>
</div>
</>
);
};

View File

@@ -13,6 +13,7 @@ import {
calculateDiffStats,
formatDiffStatsDetailed,
} from '../../../utils/diffStats.js';
import { OpenDiffIcon } from '../../icons/index.js';
import './DiffDisplay.css';
/**
@@ -119,9 +120,7 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
onClick={onOpenDiff}
title="Open in VS Code diff viewer"
>
<svg width="14" height="14" viewBox="0 0 16 16" fill="none">
<path d="M13.5 7l-4-4v3h-6v2h6v3l4-4z" fill="currentColor" />
</svg>
<OpenDiffIcon width="14" height="14" />
Open Diff
</button>
)}

View File

@@ -5,6 +5,7 @@
*/
import type React from 'react';
import { ChevronDownIcon, PlusIcon } from '../icons/index.js';
interface ChatHeaderProps {
currentSessionTitle: string;
@@ -41,19 +42,7 @@ export const ChatHeader: React.FC<ChatHeaderProps> = ({
<span style={{ fontSize: 'var(--vscode-chat-font-size, 13px)' }}>
{currentSessionTitle}
</span>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
className="w-3.5 h-3.5"
>
<path
fillRule="evenodd"
d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z"
clipRule="evenodd"
></path>
</svg>
<ChevronDownIcon className="w-3.5 h-3.5" />
</span>
</button>
@@ -94,16 +83,7 @@ export const ChatHeader: React.FC<ChatHeaderProps> = ({
onClick={onNewSession}
title="New Session"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
data-slot="icon"
className="w-4 h-4"
>
<path d="M10.75 4.75a.75.75 0 0 0-1.5 0v4.5h-4.5a.75.75 0 0 0 0 1.5h4.5v4.5a.75.75 0 0 0 1.5 0v-4.5h4.5a.75.75 0 0 0 0-1.5h-4.5v-4.5Z"></path>
</svg>
<PlusIcon className="w-4 h-4" />
</button>
</div>
);