mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-28 03:30:40 +00:00
feat(webui): render markdown in generic and web-fetch tool outputs (#3469)
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* feat(webui): render markdown in generic and web-fetch tool outputs Agent and other tool call outputs were wrapped in raw <pre> blocks, leaving markdown-formatted text (code blocks, lists, bold) as unrendered characters in HTML exports. Route long GenericToolCall output and WebFetchToolCall success output through MarkdownRenderer, and add collapse/expand affordance to long generic output for readability. Closes #2520 Co-Authored-By: Qwen-Coder <noreply@alibabacloud.com> * fix(webui): move tool-output mask-image to inline style, disable file-link in webfetch Tailwind's static scanner can't emit classes built from template-string interpolation (max-h-[${n}px], [mask-image:...] with runtime values), so the collapsed-state fade on GenericToolCall and WebFetchToolCall output cards was not actually applied at runtime — only the inline maxHeight was collapsing content. Move both max-height and mask-image to inline style, and add -webkit-mask-image for Safari. Also pass enableFileLinks={false} to MarkdownRenderer for WebFetch output so raw fetched text keeps file-like strings (README.md, URLs ending in .md, etc.) as literal output instead of converting them to local workspace links. Co-Authored-By: Qwen-Coder <noreply@alibabacloud.com> * fix(webui): disable file-link in generic tool-call output Align GenericToolCall markdown rendering with WebFetchToolCall by passing enableFileLinks={false}. Without a click handler wired, file-like strings (e.g. README.md) would render as clickable links that do nothing, which is a misleading affordance in the tool-call display/export context. Co-Authored-By: Qwen-Coder <noreply@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <noreply@alibabacloud.com>
This commit is contained in:
parent
afbb5e71db
commit
00896f8605
2 changed files with 58 additions and 19 deletions
|
|
@ -6,7 +6,7 @@
|
|||
* Generic tool call component - handles all tool call types as fallback
|
||||
*/
|
||||
|
||||
import type { FC } from 'react';
|
||||
import { useState, type FC } from 'react';
|
||||
import {
|
||||
ToolCallContainer,
|
||||
ToolCallCard,
|
||||
|
|
@ -17,6 +17,45 @@ import {
|
|||
} from './shared/index.js';
|
||||
import type { BaseToolCallProps } from './shared/index.js';
|
||||
import { getToolDisplayLabel } from './labelUtils.js';
|
||||
import { MarkdownRenderer } from '../messages/MarkdownRenderer/MarkdownRenderer.js';
|
||||
|
||||
const COLLAPSED_HEIGHT = 200;
|
||||
const EXPAND_THRESHOLD = 400;
|
||||
|
||||
const CollapsibleOutput: FC<{ content: string }> = ({ content }) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const isLongContent = content.length > EXPAND_THRESHOLD;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-[3px]">
|
||||
<div
|
||||
className="text-[13px] opacity-90 overflow-hidden"
|
||||
style={
|
||||
!isExpanded && isLongContent
|
||||
? {
|
||||
maxHeight: `${COLLAPSED_HEIGHT}px`,
|
||||
maskImage: `linear-gradient(to bottom, var(--app-primary-background) 140px, transparent ${COLLAPSED_HEIGHT}px)`,
|
||||
WebkitMaskImage: `linear-gradient(to bottom, var(--app-primary-background) 140px, transparent ${COLLAPSED_HEIGHT}px)`,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<MarkdownRenderer content={content} enableFileLinks={false} />
|
||||
</div>
|
||||
{isLongContent && (
|
||||
<div className="flex justify-center border-t border-[var(--app-input-border)] pt-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
className="text-[var(--app-secondary-foreground)] text-[0.8em] hover:text-[var(--app-primary-foreground)] cursor-pointer bg-transparent border-none px-2 py-1 rounded hover:bg-[var(--app-input-background)] transition-colors"
|
||||
>
|
||||
{isExpanded ? '▲ Collapse' : '▼ Show more'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic tool call component that can display any tool call type
|
||||
|
|
@ -55,18 +94,13 @@ export const GenericToolCall: FC<BaseToolCallProps> = ({
|
|||
const isLong = output.length > 150;
|
||||
|
||||
if (isLong) {
|
||||
const truncatedOutput =
|
||||
output.length > 300 ? output.substring(0, 300) + '...' : output;
|
||||
|
||||
return (
|
||||
<ToolCallCard icon="🔧">
|
||||
<ToolCallRow label={displayLabel}>
|
||||
<div>{operationText}</div>
|
||||
</ToolCallRow>
|
||||
<ToolCallRow label="Output">
|
||||
<div className="whitespace-pre-wrap font-mono text-[13px] opacity-90">
|
||||
{truncatedOutput}
|
||||
</div>
|
||||
<CollapsibleOutput content={output} />
|
||||
</ToolCallRow>
|
||||
</ToolCallCard>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {
|
|||
} from './shared/index.js';
|
||||
import type { BaseToolCallProps } from './shared/index.js';
|
||||
import { getToolDisplayLabel } from './labelUtils.js';
|
||||
import { MarkdownRenderer } from '../messages/MarkdownRenderer/MarkdownRenderer.js';
|
||||
|
||||
type WebVariant = 'fetch' | 'search';
|
||||
|
||||
|
|
@ -70,24 +71,28 @@ const OutputCard: FC<{
|
|||
OUT
|
||||
</div>
|
||||
<div
|
||||
className={`whitespace-pre-wrap break-words m-0 p-1 overflow-hidden ${
|
||||
!isExpanded && isLongContent
|
||||
? `max-h-[${COLLAPSED_HEIGHT}px] [mask-image:linear-gradient(to_bottom,var(--app-primary-background)_80px,transparent_${COLLAPSED_HEIGHT}px)]`
|
||||
: ''
|
||||
className={`break-words m-0 p-1 overflow-hidden ${
|
||||
isError ? 'whitespace-pre-wrap' : ''
|
||||
}`}
|
||||
style={
|
||||
!isExpanded && isLongContent
|
||||
? { maxHeight: `${COLLAPSED_HEIGHT}px` }
|
||||
? {
|
||||
maxHeight: `${COLLAPSED_HEIGHT}px`,
|
||||
maskImage: `linear-gradient(to bottom, var(--app-primary-background) 80px, transparent ${COLLAPSED_HEIGHT}px)`,
|
||||
WebkitMaskImage: `linear-gradient(to bottom, var(--app-primary-background) 80px, transparent ${COLLAPSED_HEIGHT}px)`,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<pre
|
||||
className={`m-0 overflow-hidden font-mono text-[0.85em] ${
|
||||
isError ? 'text-[#c74e39]' : ''
|
||||
}`}
|
||||
>
|
||||
{content}
|
||||
</pre>
|
||||
{isError ? (
|
||||
<pre className="m-0 overflow-hidden font-mono text-[0.85em] text-[#c74e39]">
|
||||
{content}
|
||||
</pre>
|
||||
) : (
|
||||
<div className="text-[0.85em]">
|
||||
<MarkdownRenderer content={content} enableFileLinks={false} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue