mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-21 09:17:53 +00:00
feat(vscode-ide-companion): add cancel streaming functionality
- Add handleCancel callback to App component - Implement cancelStreaming message posting to VS Code - Add onCancel prop to InputForm component - Replace send button with stop button during streaming
This commit is contained in:
@@ -10,7 +10,9 @@ import type React from 'react';
|
||||
import type { BaseToolCallProps } from '../shared/types.js';
|
||||
import { ToolCallContainer } from '../shared/LayoutComponents.js';
|
||||
import { safeTitle, groupContent } from '../shared/utils.js';
|
||||
import './ExecuteToolCall.css';
|
||||
import { useVSCode } from '../../../hooks/useVSCode.js';
|
||||
import { createAndOpenTempFile } from '../../../utils/tempFileManager.js';
|
||||
import './Bash.css';
|
||||
|
||||
/**
|
||||
* Specialized component for Execute/Bash tool calls
|
||||
@@ -19,6 +21,7 @@ import './ExecuteToolCall.css';
|
||||
export const ExecuteToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
||||
const { title, content, rawInput, toolCallId } = toolCall;
|
||||
const commandText = safeTitle(title);
|
||||
const vscode = useVSCode();
|
||||
|
||||
// Group content by type
|
||||
const { textOutputs, errors } = groupContent(content);
|
||||
@@ -32,6 +35,24 @@ export const ExecuteToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
||||
inputCommand = rawInput;
|
||||
}
|
||||
|
||||
// Handle click on IN section
|
||||
const handleInClick = () => {
|
||||
createAndOpenTempFile(
|
||||
vscode.postMessage,
|
||||
inputCommand,
|
||||
'bash-input',
|
||||
'.sh',
|
||||
);
|
||||
};
|
||||
|
||||
// Handle click on OUT section
|
||||
const handleOutClick = () => {
|
||||
if (textOutputs.length > 0) {
|
||||
const output = textOutputs.join('\n');
|
||||
createAndOpenTempFile(vscode.postMessage, output, 'bash-output', '.txt');
|
||||
}
|
||||
};
|
||||
|
||||
// Error case
|
||||
if (errors.length > 0) {
|
||||
return (
|
||||
@@ -45,7 +66,11 @@ export const ExecuteToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
||||
<div className="bash-toolcall-card">
|
||||
<div className="bash-toolcall-content">
|
||||
{/* IN row */}
|
||||
<div className="bash-toolcall-row">
|
||||
<div
|
||||
className="bash-toolcall-row"
|
||||
onClick={handleInClick}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
<div className="bash-toolcall-label">IN</div>
|
||||
<div className="bash-toolcall-row-content">
|
||||
<pre className="bash-toolcall-pre">{inputCommand}</pre>
|
||||
@@ -84,7 +109,11 @@ export const ExecuteToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
||||
<div className="bash-toolcall-card">
|
||||
<div className="bash-toolcall-content">
|
||||
{/* IN row */}
|
||||
<div className="bash-toolcall-row">
|
||||
<div
|
||||
className="bash-toolcall-row"
|
||||
onClick={handleInClick}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
<div className="bash-toolcall-label">IN</div>
|
||||
<div className="bash-toolcall-row-content">
|
||||
<pre className="bash-toolcall-pre">{inputCommand}</pre>
|
||||
@@ -92,7 +121,11 @@ export const ExecuteToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
||||
</div>
|
||||
|
||||
{/* OUT row */}
|
||||
<div className="bash-toolcall-row">
|
||||
<div
|
||||
className="bash-toolcall-row"
|
||||
onClick={handleOutClick}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
<div className="bash-toolcall-label">OUT</div>
|
||||
<div className="bash-toolcall-row-content">
|
||||
<div className="bash-toolcall-output-subtle">
|
||||
@@ -109,7 +142,11 @@ export const ExecuteToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
||||
// Success without output: show command with branch connector
|
||||
return (
|
||||
<ToolCallContainer label="Bash" status="success" toolCallId={toolCallId}>
|
||||
<div className="inline-flex text-[var(--app-secondary-foreground)] text-[0.85em] opacity-70 mt-[2px] mb-[2px] flex-row items-start w-full gap-1">
|
||||
<div
|
||||
className="inline-flex text-[var(--app-secondary-foreground)] text-[0.85em] opacity-70 mt-[2px] mb-[2px] flex-row items-start w-full gap-1"
|
||||
onClick={handleInClick}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
<span className="flex-shrink-0 relative top-[-0.1em]">⎿</span>
|
||||
<span className="flex-shrink-0 w-full">{commandText}</span>
|
||||
</div>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
*/
|
||||
|
||||
import { useEffect, useCallback } from 'react';
|
||||
import type { BaseToolCallProps } from './shared/types.js';
|
||||
import { ToolCallContainer } from './shared/LayoutComponents.js';
|
||||
import { DiffDisplay } from './shared/DiffDisplay.js';
|
||||
import { groupContent } from './shared/utils.js';
|
||||
import { useVSCode } from '../../hooks/useVSCode.js';
|
||||
import { FileLink } from '../ui/FileLink.js';
|
||||
import type { BaseToolCallProps } from '../shared/types.js';
|
||||
import { ToolCallContainer } from '../shared/LayoutComponents.js';
|
||||
import { DiffDisplay } from '../shared/DiffDisplay.js';
|
||||
import { groupContent } from '../shared/utils.js';
|
||||
import { useVSCode } from '../../../hooks/useVSCode.js';
|
||||
import { FileLink } from '../../ui/FileLink.js';
|
||||
|
||||
/**
|
||||
* Calculate diff summary (added/removed lines)
|
||||
@@ -76,11 +76,13 @@ export const EditToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
||||
firstDiff.oldText !== undefined &&
|
||||
firstDiff.newText !== undefined
|
||||
) {
|
||||
// TODO: 暂时注释
|
||||
// Add a small delay to ensure the component is fully rendered
|
||||
const timer = setTimeout(() => {
|
||||
handleOpenDiff(path, firstDiff.oldText, firstDiff.newText);
|
||||
}, 100);
|
||||
return () => clearTimeout(timer);
|
||||
// const timer = setTimeout(() => {
|
||||
// handleOpenDiff(path, firstDiff.oldText, firstDiff.newText);
|
||||
// }, 100);
|
||||
let timer;
|
||||
return () => timer && clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
}, [diffs, locations, handleOpenDiff]);
|
||||
@@ -120,48 +122,47 @@ export const EditToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
||||
handleOpenDiff(path, firstDiff.oldText, firstDiff.newText);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className="relative py-2 select-text cursor-pointer hover:bg-[var(--app-input-background)] toolcall-container toolcall-status-success"
|
||||
onClick={openFirstDiff}
|
||||
title="Open diff in VS Code"
|
||||
>
|
||||
{/* Keep content within overall width: pl-[30px] provides the bullet indent; */}
|
||||
{/* IMPORTANT: Always include min-w-0/max-w-full on inner wrappers to prevent overflow. */}
|
||||
<div className="toolcall-edit-content flex flex-col gap-1 pl-[30px] min-w-0 max-w-full">
|
||||
<div className="flex items-center justify-between min-w-0">
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
{/* Align the inline Edit label styling with shared toolcall label: larger + bold */}
|
||||
<span className="text-[14px] leading-none font-bold text-[var(--app-primary-foreground)]">
|
||||
Edit
|
||||
</span>
|
||||
{path && (
|
||||
<FileLink
|
||||
path={path}
|
||||
showFullPath={false}
|
||||
className="text-xs font-mono text-[var(--app-secondary-foreground)] hover:underline"
|
||||
/>
|
||||
)}
|
||||
{/* {toolCallId && (
|
||||
<div
|
||||
className="qwen-message message-item relative py-2 select-text cursor-pointer hover:bg-[var(--app-input-background)] toolcall-container toolcall-status-success"
|
||||
onClick={openFirstDiff}
|
||||
title="Open diff in VS Code"
|
||||
>
|
||||
{/* Keep content within overall width: pl-[30px] provides the bullet indent; */}
|
||||
{/* IMPORTANT: Always include min-w-0/max-w-full on inner wrappers to prevent overflow. */}
|
||||
<div className="toolcall-edit-content flex flex-col gap-1 min-w-0 max-w-full pl-[30px]">
|
||||
<div className="flex items-center justify-between min-w-0">
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
{/* Align the inline Edit label styling with shared toolcall label: larger + bold */}
|
||||
<span className="text-[14px] leading-none font-bold text-[var(--app-primary-foreground)]">
|
||||
Edit
|
||||
</span>
|
||||
{path && (
|
||||
<FileLink
|
||||
path={path}
|
||||
showFullPath={false}
|
||||
className="text-xs font-mono text-[var(--app-secondary-foreground)] hover:underline"
|
||||
/>
|
||||
)}
|
||||
{/* {toolCallId && (
|
||||
<span className="text-[10px] opacity-30">
|
||||
[{toolCallId.slice(-8)}]
|
||||
</span>
|
||||
)} */}
|
||||
</div>
|
||||
<span className="text-xs opacity-60 ml-2">open</span>
|
||||
</div>
|
||||
<div className="inline-flex text-[var(--app-secondary-foreground)] text-[0.85em] opacity-70 flex-row items-start w-full gap-1">
|
||||
<span className="flex-shrink-0 relative top-[-0.1em]">⎿</span>
|
||||
<span className="flex-shrink-0 w-full">{summary}</span>
|
||||
</div>
|
||||
<span className="text-xs opacity-60 ml-2">open</span>
|
||||
</div>
|
||||
<div className="inline-flex text-[var(--app-secondary-foreground)] text-[0.85em] opacity-70 flex-row items-start w-full gap-1">
|
||||
<span className="flex-shrink-0 relative top-[-0.1em]">⎿</span>
|
||||
<span className="flex-shrink-0 w-full">{summary}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content area aligned with bullet indent. Do NOT exceed container width. */}
|
||||
{/* For any custom blocks here, keep: min-w-0 max-w-full and avoid extra horizontal padding/margins. */}
|
||||
<div className="pl-[30px] mt-1 min-w-0 max-w-full overflow-hidden">
|
||||
{diffs.map(
|
||||
(
|
||||
item: import('./shared/types.js').ToolCallContent,
|
||||
item: import('../shared/types.js').ToolCallContent,
|
||||
idx: number,
|
||||
) => (
|
||||
<DiffDisplay
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Qwen Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Execute tool call styles - Enhanced styling with semantic class names
|
||||
*/
|
||||
|
||||
/* Root container for execute tool call output */
|
||||
.execute-toolcall-card {
|
||||
border: 0.5px solid var(--app-input-border);
|
||||
border-radius: 5px;
|
||||
background: var(--app-tool-background);
|
||||
margin: 8px 0;
|
||||
max-width: 100%;
|
||||
font-size: 1em;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
/* Content wrapper inside the card */
|
||||
.execute-toolcall-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
/* Individual input/output row */
|
||||
.execute-toolcall-row {
|
||||
display: grid;
|
||||
grid-template-columns: max-content 1fr;
|
||||
border-top: 0.5px solid var(--app-input-border);
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
/* First row has no top border */
|
||||
.execute-toolcall-row:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
/* Row label (IN/OUT/ERROR) */
|
||||
.execute-toolcall-label {
|
||||
grid-column: 1;
|
||||
color: var(--app-secondary-foreground);
|
||||
text-align: left;
|
||||
opacity: 50%;
|
||||
padding: 4px 8px 4px 4px;
|
||||
font-family: var(--app-monospace-font-family);
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
/* Row content area */
|
||||
.execute-toolcall-row-content {
|
||||
grid-column: 2;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
/* Truncated content styling */
|
||||
.execute-toolcall-row-content:not(.execute-toolcall-full) {
|
||||
max-height: 60px;
|
||||
mask-image: linear-gradient(
|
||||
to bottom,
|
||||
var(--app-primary-background) 40px,
|
||||
transparent 60px
|
||||
);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Preformatted content */
|
||||
.execute-toolcall-pre {
|
||||
margin-block: 0;
|
||||
overflow: hidden;
|
||||
font-family: var(--app-monospace-font-family);
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
/* Code content */
|
||||
.execute-toolcall-code {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: var(--app-monospace-font-family);
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
/* Output content with subtle styling */
|
||||
.execute-toolcall-output-subtle {
|
||||
background-color: var(--app-code-background);
|
||||
white-space: pre;
|
||||
overflow-x: auto;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Error content styling */
|
||||
.execute-toolcall-error-content {
|
||||
color: #c74e39;
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Qwen Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Execute tool call component - specialized for command execution operations
|
||||
*/
|
||||
|
||||
import type React from 'react';
|
||||
import type { BaseToolCallProps } from '../shared/types.js';
|
||||
import { ToolCallContainer } from '../shared/LayoutComponents.js';
|
||||
import { safeTitle, groupContent } from '../shared/utils.js';
|
||||
import './Execute.css';
|
||||
|
||||
/**
|
||||
* Specialized component for Execute tool calls
|
||||
* Shows: Execute bullet + description + IN/OUT card
|
||||
*/
|
||||
export const ExecuteToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
||||
const { title, content, rawInput, toolCallId } = toolCall;
|
||||
const commandText = safeTitle(title);
|
||||
|
||||
// Group content by type
|
||||
const { textOutputs, errors } = groupContent(content);
|
||||
|
||||
// Extract command from rawInput if available
|
||||
let inputCommand = commandText;
|
||||
if (rawInput && typeof rawInput === 'object') {
|
||||
const inputObj = rawInput as { command?: string };
|
||||
inputCommand = inputObj.command || commandText;
|
||||
} else if (typeof rawInput === 'string') {
|
||||
inputCommand = rawInput;
|
||||
}
|
||||
|
||||
// Error case
|
||||
if (errors.length > 0) {
|
||||
return (
|
||||
<ToolCallContainer label="Execute" status="error" toolCallId={toolCallId}>
|
||||
{/* Branch connector summary (Claude-like) */}
|
||||
<div className="inline-flex text-[var(--app-secondary-foreground)] text-[0.85em] opacity-70 mt-[2px] mb-[2px] flex-row items-start w-full gap-1">
|
||||
<span className="flex-shrink-0 relative top-[-0.1em]">⎿</span>
|
||||
<span className="flex-shrink-0 w-full">{commandText}</span>
|
||||
</div>
|
||||
{/* Error card - semantic DOM + Tailwind styles */}
|
||||
<div className="execute-toolcall-card">
|
||||
<div className="execute-toolcall-content">
|
||||
{/* IN row */}
|
||||
<div className="execute-toolcall-row">
|
||||
<div className="execute-toolcall-label">IN</div>
|
||||
<div className="execute-toolcall-row-content">
|
||||
<pre className="execute-toolcall-pre">{inputCommand}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ERROR row */}
|
||||
<div className="execute-toolcall-row">
|
||||
<div className="execute-toolcall-label">Error</div>
|
||||
<div className="execute-toolcall-row-content">
|
||||
<pre className="execute-toolcall-pre execute-toolcall-error-content">
|
||||
{errors.join('\n')}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ToolCallContainer>
|
||||
);
|
||||
}
|
||||
|
||||
// Success with output
|
||||
if (textOutputs.length > 0) {
|
||||
const output = textOutputs.join('\n');
|
||||
const truncatedOutput =
|
||||
output.length > 500 ? output.substring(0, 500) + '...' : output;
|
||||
|
||||
return (
|
||||
<ToolCallContainer
|
||||
label="Execute"
|
||||
status="success"
|
||||
toolCallId={toolCallId}
|
||||
>
|
||||
{/* Branch connector summary (Claude-like) */}
|
||||
<div className="inline-flex text-[var(--app-secondary-foreground)] text-[0.85em] opacity-70 mt-[2px] mb-[2px] flex-row items-start w-full gap-1">
|
||||
<span className="flex-shrink-0 relative top-[-0.1em]">⎿</span>
|
||||
<span className="flex-shrink-0 w-full">{commandText}</span>
|
||||
</div>
|
||||
{/* Output card - semantic DOM + Tailwind styles */}
|
||||
<div className="execute-toolcall-card">
|
||||
<div className="execute-toolcall-content">
|
||||
{/* IN row */}
|
||||
<div className="execute-toolcall-row">
|
||||
<div className="execute-toolcall-label">IN</div>
|
||||
<div className="execute-toolcall-row-content">
|
||||
<pre className="execute-toolcall-pre">{inputCommand}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* OUT row */}
|
||||
<div className="execute-toolcall-row">
|
||||
<div className="execute-toolcall-label">OUT</div>
|
||||
<div className="execute-toolcall-row-content">
|
||||
<div className="execute-toolcall-output-subtle">
|
||||
<pre className="execute-toolcall-pre">{truncatedOutput}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ToolCallContainer>
|
||||
);
|
||||
}
|
||||
|
||||
// Success without output: show command with branch connector
|
||||
return (
|
||||
<ToolCallContainer label="Execute" status="success" toolCallId={toolCallId}>
|
||||
<div className="inline-flex text-[var(--app-secondary-foreground)] text-[0.85em] opacity-70 mt-[2px] mb-[2px] flex-row items-start w-full gap-1">
|
||||
<span className="flex-shrink-0 relative top-[-0.1em]">⎿</span>
|
||||
<span className="flex-shrink-0 w-full">{commandText}</span>
|
||||
</div>
|
||||
</ToolCallContainer>
|
||||
);
|
||||
};
|
||||
@@ -12,8 +12,9 @@ import { shouldShowToolCall } from './shared/utils.js';
|
||||
import { GenericToolCall } from './GenericToolCall.js';
|
||||
import { ReadToolCall } from './ReadToolCall.js';
|
||||
import { WriteToolCall } from './WriteToolCall.js';
|
||||
import { EditToolCall } from './EditToolCall.js';
|
||||
import { ExecuteToolCall } from './Bash/Bash.js';
|
||||
import { EditToolCall } from './Edit/EditToolCall.js';
|
||||
import { ExecuteToolCall as BashExecuteToolCall } from './Bash/Bash.js';
|
||||
import { ExecuteToolCall } from './Execute/Execute.js';
|
||||
import { SearchToolCall } from './SearchToolCall.js';
|
||||
import { ThinkToolCall } from './ThinkToolCall.js';
|
||||
import { TodoWriteToolCall } from './TodoWriteToolCall.js';
|
||||
@@ -38,9 +39,11 @@ export const getToolCallComponent = (
|
||||
return EditToolCall;
|
||||
|
||||
case 'execute':
|
||||
return ExecuteToolCall;
|
||||
|
||||
case 'bash':
|
||||
case 'command':
|
||||
return ExecuteToolCall;
|
||||
return BashExecuteToolCall;
|
||||
|
||||
case 'search':
|
||||
case 'grep':
|
||||
|
||||
@@ -153,8 +153,53 @@
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Flex container with margin bottom */
|
||||
/* ToolCall header with loading indicator */
|
||||
.toolcall-header {
|
||||
/* TODO: 应该不需要? 待删除 */
|
||||
/* margin-bottom: 12px; */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.toolcall-header::before {
|
||||
content: '\25cf';
|
||||
position: absolute;
|
||||
left: -22px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 10px;
|
||||
line-height: 1;
|
||||
z-index: 1;
|
||||
color: #e1c08d;
|
||||
animation: toolcallHeaderPulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Loading animation for toolcall header */
|
||||
@keyframes toolcallHeaderPulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
/* In-progress toolcall specific styles */
|
||||
.in-progress-toolcall .toolcall-content-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.in-progress-toolcall .toolcall-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2;
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.in-progress-toolcall .toolcall-content-text {
|
||||
word-break: break-word;
|
||||
white-space: pre-wrap;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -43,11 +43,11 @@ export const ToolCallContainer: React.FC<ToolCallContainerProps> = ({
|
||||
labelSuffix,
|
||||
}) => (
|
||||
<div
|
||||
className={`relative pl-[30px] py-2 select-text toolcall-container toolcall-status-${status}`}
|
||||
className={`qwen-message relative pl-[30px] py-2 select-text toolcall-container toolcall-status-${status}`}
|
||||
>
|
||||
{/* Timeline connector line using ::after pseudo-element */}
|
||||
<div className="toolcall-content-wrapper flex flex-col gap-1 min-w-0 max-w-full">
|
||||
<div className="flex items-center gap-2 relative min-w-0 toolcall-header">
|
||||
<div className="flex items-center gap-2 relative min-w-0">
|
||||
<span className="text-[14px] leading-none font-bold text-[var(--app-primary-foreground)]">
|
||||
{label}
|
||||
</span>
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
* Copyright 2025 Qwen Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Simplified timeline styles for tool calls
|
||||
* Simplified timeline styles for tool calls and messages
|
||||
* Merged version of both SimpleTimeline.css files
|
||||
*/
|
||||
|
||||
/* Basic timeline container */
|
||||
.simple-toolcall-container {
|
||||
.simple-toolcall-container,
|
||||
.simple-timeline-container {
|
||||
position: relative;
|
||||
padding-left: 30px;
|
||||
padding-top: 8px;
|
||||
@@ -15,7 +17,8 @@
|
||||
}
|
||||
|
||||
/* Timeline connector - simple version */
|
||||
.simple-toolcall-container::after {
|
||||
.simple-toolcall-container::after,
|
||||
.simple-timeline-container::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
@@ -26,19 +29,22 @@
|
||||
}
|
||||
|
||||
/* First item connector starts lower */
|
||||
.simple-toolcall-container:first-child::after {
|
||||
.simple-toolcall-container:first-child::after,
|
||||
.simple-timeline-container:first-child::after {
|
||||
top: 24px;
|
||||
}
|
||||
|
||||
/* Last item connector ends higher */
|
||||
.simple-toolcall-container:last-child::after {
|
||||
.simple-toolcall-container:last-child::after,
|
||||
.simple-timeline-container:last-child::after {
|
||||
height: calc(100% - 24px);
|
||||
top: 0;
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
/* Bullet point */
|
||||
.simple-toolcall-container::before {
|
||||
.simple-toolcall-container::before,
|
||||
.simple-timeline-container::before {
|
||||
content: '\25cf';
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
@@ -46,4 +52,4 @@
|
||||
font-size: 10px;
|
||||
color: var(--app-secondary-foreground);
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user