-
Notifications
You must be signed in to change notification settings - Fork 221
fix: hide empty tool input card and remove user prompt copy button #165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f5dcbfd
e9f3b36
213ba21
aa1df1e
09fb97f
b315a64
c951a19
c322cc4
c3cb448
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| /** | ||
| * BashToolViewer | ||
| * | ||
| * Renders Bash tool calls with syntax-highlighted command input | ||
| * via CodeBlockViewer and collapsible output section. | ||
| */ | ||
|
|
||
| import React from 'react'; | ||
|
|
||
| import { CodeBlockViewer } from '@renderer/components/chat/viewers'; | ||
|
|
||
| import { type ItemStatus } from '../BaseItem'; | ||
|
|
||
| import { CollapsibleOutputSection } from './CollapsibleOutputSection'; | ||
| import { renderOutput } from './renderHelpers'; | ||
|
|
||
| import type { LinkedToolItem } from '@renderer/types/groups'; | ||
|
|
||
| interface BashToolViewerProps { | ||
| linkedTool: LinkedToolItem; | ||
| status: ItemStatus; | ||
| } | ||
|
|
||
| export const BashToolViewer: React.FC<BashToolViewerProps> = ({ linkedTool, status }) => { | ||
| const command = linkedTool.input.command as string; | ||
| const description = linkedTool.input.description as string | undefined; | ||
|
|
||
| // Use the description (truncated) as the file name label, or fallback to "bash" | ||
| const fileName = description | ||
| ? description.length > 60 | ||
| ? description.slice(0, 57) + '...' | ||
| : description | ||
| : 'bash'; | ||
|
|
||
| return ( | ||
| <> | ||
| {/* Input Section — Syntax-highlighted command */} | ||
| <CodeBlockViewer | ||
| fileName={fileName} | ||
| content={command} | ||
| language="bash" | ||
| /> | ||
|
Comment on lines
+25
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: fd -t f "BashToolViewer.tsx"Repository: matt1398/claude-devtools Length of output: 132 🏁 Script executed: fd -t f "LinkedToolItem" --type fRepository: matt1398/claude-devtools Length of output: 121 🏁 Script executed: fd -t f "toolContentChecks.ts"Repository: matt1398/claude-devtools Length of output: 121 🏁 Script executed: cat -n src/renderer/components/chat/items/linkedTool/BashToolViewer.tsxRepository: matt1398/claude-devtools Length of output: 1886 🏁 Script executed: cat -n src/renderer/components/chat/items/LinkedToolItem.tsx | head -80Repository: matt1398/claude-devtools Length of output: 3032 🏁 Script executed: cat -n src/renderer/utils/toolRendering/toolContentChecks.tsRepository: matt1398/claude-devtools Length of output: 2530 Guard tool input shape before rendering command content. Lines 25-26 use unchecked casts ( Suggested patch interface BashToolViewerProps {
linkedTool: LinkedToolItem;
status: ItemStatus;
}
+interface BashInput {
+ command: string;
+ description?: string;
+}
+
+function isBashInput(input: Record<string, unknown>): input is BashInput {
+ return (
+ typeof input.command === 'string' &&
+ (input.description === undefined || typeof input.description === 'string')
+ );
+}
+
export const BashToolViewer: React.FC<BashToolViewerProps> = ({ linkedTool, status }) => {
- const command = linkedTool.input.command as string;
- const description = linkedTool.input.description as string | undefined;
+ if (!isBashInput(linkedTool.input) || linkedTool.input.command.trim().length === 0) {
+ return null;
+ }
+
+ const { command, description } = linkedTool.input;
// Use the description (truncated) as the file name label, or fallback to "bash"
const fileName = descriptionPer coding guidelines, 🤖 Prompt for AI Agents |
||
|
|
||
| {/* Output Section — Collapsible */} | ||
| {!linkedTool.isOrphaned && linkedTool.result && ( | ||
| <CollapsibleOutputSection status={status}> | ||
| {renderOutput(linkedTool.result.content)} | ||
| </CollapsibleOutputSection> | ||
| )} | ||
| </> | ||
| ); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| /** | ||
| * CollapsibleOutputSection | ||
| * | ||
| * Reusable component that wraps tool output in a collapsed-by-default section. | ||
| * Shows a clickable header with label, StatusDot, and chevron toggle. | ||
| */ | ||
|
|
||
| import React, { useState } from 'react'; | ||
|
|
||
| import { ChevronDown, ChevronRight } from 'lucide-react'; | ||
|
|
||
| import { type ItemStatus, StatusDot } from '../BaseItem'; | ||
|
|
||
| interface CollapsibleOutputSectionProps { | ||
| status: ItemStatus; | ||
| children: React.ReactNode; | ||
| /** Label shown in the header (default: "Output") */ | ||
| label?: string; | ||
| } | ||
|
|
||
| export const CollapsibleOutputSection: React.FC<CollapsibleOutputSectionProps> = ({ | ||
| status, | ||
| children, | ||
| label = 'Output', | ||
| }) => { | ||
| const [isExpanded, setIsExpanded] = useState(false); | ||
|
|
||
| return ( | ||
| <div> | ||
| <button | ||
| type="button" | ||
| className="mb-1 flex items-center gap-2 text-xs" | ||
| style={{ color: 'var(--tool-item-muted)', background: 'none', border: 'none', padding: 0, cursor: 'pointer' }} | ||
| onClick={() => setIsExpanded((prev) => !prev)} | ||
| > | ||
| {isExpanded ? <ChevronDown className="size-3" /> : <ChevronRight className="size-3" />} | ||
| {label} | ||
| <StatusDot status={status} /> | ||
| </button> | ||
| {isExpanded && ( | ||
| <div | ||
| className="max-h-96 overflow-auto rounded p-3 font-mono text-xs" | ||
| style={{ | ||
| backgroundColor: 'var(--code-bg)', | ||
| border: '1px solid var(--code-border)', | ||
| color: | ||
| status === 'error' | ||
| ? 'var(--tool-result-error-text)' | ||
| : 'var(--color-text-secondary)', | ||
| }} | ||
| > | ||
| {children} | ||
| </div> | ||
| )} | ||
| </div> | ||
| ); | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -6,8 +6,9 @@ | |||||
|
|
||||||
| import React from 'react'; | ||||||
|
|
||||||
| import { type ItemStatus, StatusDot } from '../BaseItem'; | ||||||
| import { type ItemStatus } from '../BaseItem'; | ||||||
|
|
||||||
| import { CollapsibleOutputSection } from './CollapsibleOutputSection'; | ||||||
| import { renderInput, renderOutput } from './renderHelpers'; | ||||||
|
|
||||||
| import type { LinkedToolItem } from '@renderer/types/groups'; | ||||||
|
|
@@ -18,50 +19,35 @@ interface DefaultToolViewerProps { | |||||
| } | ||||||
|
|
||||||
| export const DefaultToolViewer: React.FC<DefaultToolViewerProps> = ({ linkedTool, status }) => { | ||||||
| const hasInput = Object.keys(linkedTool.input).length > 0; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Accessing
Suggested change
|
||||||
|
|
||||||
| return ( | ||||||
| <> | ||||||
| {/* Input Section */} | ||||||
| <div> | ||||||
| <div className="mb-1 text-xs" style={{ color: 'var(--tool-item-muted)' }}> | ||||||
| Input | ||||||
| </div> | ||||||
| <div | ||||||
| className="max-h-96 overflow-auto rounded p-3 font-mono text-xs" | ||||||
| style={{ | ||||||
| backgroundColor: 'var(--code-bg)', | ||||||
| border: '1px solid var(--code-border)', | ||||||
| color: 'var(--color-text-secondary)', | ||||||
| }} | ||||||
| > | ||||||
| {renderInput(linkedTool.name, linkedTool.input)} | ||||||
| </div> | ||||||
| </div> | ||||||
|
|
||||||
| {/* Output Section */} | ||||||
| {!linkedTool.isOrphaned && linkedTool.result && ( | ||||||
| {hasInput && ( | ||||||
| <div> | ||||||
| <div | ||||||
| className="mb-1 flex items-center gap-2 text-xs" | ||||||
| style={{ color: 'var(--tool-item-muted)' }} | ||||||
| > | ||||||
| Output | ||||||
| <StatusDot status={status} /> | ||||||
| <div className="mb-1 text-xs" style={{ color: 'var(--tool-item-muted)' }}> | ||||||
| Input | ||||||
| </div> | ||||||
| <div | ||||||
| className="max-h-96 overflow-auto rounded p-3 font-mono text-xs" | ||||||
| style={{ | ||||||
| backgroundColor: 'var(--code-bg)', | ||||||
| border: '1px solid var(--code-border)', | ||||||
| color: | ||||||
| status === 'error' | ||||||
| ? 'var(--tool-result-error-text)' | ||||||
| : 'var(--color-text-secondary)', | ||||||
| color: 'var(--color-text-secondary)', | ||||||
| }} | ||||||
| > | ||||||
| {renderOutput(linkedTool.result.content)} | ||||||
| {renderInput(linkedTool.name, linkedTool.input)} | ||||||
| </div> | ||||||
| </div> | ||||||
| )} | ||||||
|
|
||||||
| {/* Output Section — Collapsed by default */} | ||||||
| {!linkedTool.isOrphaned && linkedTool.result && ( | ||||||
| <CollapsibleOutputSection status={status}> | ||||||
| {renderOutput(linkedTool.result.content)} | ||||||
| </CollapsibleOutputSection> | ||||||
| )} | ||||||
| </> | ||||||
| ); | ||||||
| }; | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reorder imports to match the project’s import grouping rule.
Line 17 imports a path alias after relative imports. Move
import type { LinkedToolItem } from '@renderer/types/groups';above the../and./imports.As per coding guidelines,
**/*.{ts,tsx}requires import order: external packages, path aliases, then relative imports.🤖 Prompt for AI Agents