Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions apps/web/src/components/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4993,8 +4993,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
onPaste={onComposerPaste}
placeholder={
isComposerApprovalState
? (activePendingApproval?.detail ??
"Resolve this approval request to continue")
? "Resolve this approval request to continue"
: activePendingProgress
? "Type your own answer, or leave this blank to use the selected option"
: showPlanFollowUpPrompt && activeProposedPlan
Expand Down
5 changes: 2 additions & 3 deletions apps/web/src/components/DiffPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ function DiffFileSection(props: {
<Button
size="icon-xs"
variant="ghost"
aria-label={collapsed ? `Expand ${filePath}` : `Collapse ${filePath}`}
aria-label={collapsed ? "Expand file diff" : "Collapse file diff"}
aria-expanded={!collapsed}
onClick={() => onToggleCollapsed(filePath)}
className="text-muted-foreground/80"
Expand All @@ -221,9 +221,8 @@ function DiffFileSection(props: {
type="button"
className="min-w-0 flex-1 truncate text-left font-mono text-[11px] text-foreground/90 underline-offset-2 hover:underline"
onClick={() => onOpenInEditor(filePath)}
title={`Open ${filePath}`}
>
{filePath}
{filePath.split("/").pop() ?? filePath}
</button>
{hasNonZeroStat(stats) && (
<span className="hidden shrink-0 font-mono text-[10px] tabular-nums text-muted-foreground/80 sm:inline">
Expand Down
1 change: 0 additions & 1 deletion apps/web/src/components/GitActionsControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -933,7 +933,6 @@ export default function GitActionsControl({ gitCwd, activeThreadId }: GitActions
loading: { title: "Opening file...", data: threadToastData },
success: () => ({
title: "Opened conflicted file",
description: filePath,
data: threadToastData,
}),
error: (error) => ({
Expand Down
1 change: 0 additions & 1 deletion apps/web/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,6 @@ export default function Sidebar() {
toastManager.add({
type: "success",
title: "Path copied",
description: ctx.path,
});
},
onError: (error) => {
Expand Down
3 changes: 0 additions & 3 deletions apps/web/src/components/WorkspaceFileTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ export const WorkspaceFileTree = memo(function WorkspaceFileTree(props: {
toastManager.add({
type: "success",
title: "Path copied",
description: ctx.path,
});
},
onError: (error) => {
Expand Down Expand Up @@ -436,7 +435,6 @@ const WorkspaceSearchResultRow = memo(function WorkspaceSearchResultRow(props: {
props.onOpenFile(props.entry.path, { metaKey: event.metaKey, ctrlKey: event.ctrlKey });
}}
onContextMenu={handleContextMenu}
title={props.entry.path}
>
<span className="mt-0.5 shrink-0">
{isDirectory ? (
Expand Down Expand Up @@ -697,7 +695,6 @@ const WorkspaceFileRow = memo(function WorkspaceFileRow(props: {
props.onOpenFile(props.entry.path, { metaKey: event.metaKey, ctrlKey: event.ctrlKey })
}
onContextMenu={handleContextMenu}
title={props.entry.path}
>
<span aria-hidden="true" className="size-3.5 shrink-0" />
<VscodeEntryIcon
Expand Down
50 changes: 31 additions & 19 deletions apps/web/src/components/chat/MessagesTimeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -984,17 +984,29 @@ function workToneClass(tone: "thinking" | "tool" | "info" | "error"): string {
return "text-muted-foreground/40";
}

/**
* Returns a privacy-safe preview string for a collapsed work entry.
*
* - Commands: only the base command name (e.g. "git", "npm"), never arguments or paths.
* - File changes: only basenames of changed files, never full paths.
* - Tool details (JSON params, etc.): omitted entirely to prevent leaking
* file paths, usernames, or other personal data during livestreams.
*/
function workEntryPreview(
workEntry: Pick<TimelineWorkEntry, "detail" | "command" | "changedFiles">,
) {
if (workEntry.command) return workEntry.command;
if (workEntry.detail) return workEntry.detail;
if (workEntry.command) {
// Only show the base command name (first token), not arguments or subcommands.
const baseCommand = workEntry.command.trim().split(/\s+/)[0];
return baseCommand || null;
}
// Intentionally skip workEntry.detail — it may contain serialized tool inputs
// with full file paths, environment info, or other sensitive data.
if ((workEntry.changedFiles?.length ?? 0) === 0) return null;
const [firstPath] = workEntry.changedFiles ?? [];
if (!firstPath) return null;
return workEntry.changedFiles!.length === 1
? firstPath
: `${firstPath} +${workEntry.changedFiles!.length - 1} more`;
const basenames = workEntry.changedFiles!.map((p) => p.split("/").pop() ?? p);
const [first] = basenames;
if (!first) return null;
return basenames.length === 1 ? first : `${first} +${basenames.length - 1} more`;
}

function workEntryIcon(workEntry: TimelineWorkEntry): LucideIcon {
Expand Down Expand Up @@ -1083,26 +1095,27 @@ const SimpleWorkEntryRow = memo(function SimpleWorkEntryRow(props: {
workToneClass(workEntry.tone),
preview ? "text-muted-foreground/70" : "",
)}
title={displayText}
>
<span className={cn("text-foreground/80", workToneClass(workEntry.tone))}>
{heading}
</span>
{preview && <span className="text-muted-foreground/55"> - {preview}</span>}
{preview && <span className="text-muted-foreground/55"> {preview}</span>}
</p>
</div>
</div>
{hasChangedFiles && !previewIsChangedFiles && (
<div className="mt-1 flex flex-wrap gap-1 pl-6">
{workEntry.changedFiles?.slice(0, 4).map((filePath) => (
<span
key={`${workEntry.id}:${filePath}`}
className="rounded-md border border-border/55 bg-background/75 px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground/75"
title={filePath}
>
{filePath}
</span>
))}
{workEntry.changedFiles?.slice(0, 4).map((filePath) => {
const basename = filePath.split("/").pop() ?? filePath;
return (
<span
key={`${workEntry.id}:${filePath}`}
className="rounded-md border border-border/55 bg-background/75 px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground/75"
>
{basename}
</span>
);
})}
{(workEntry.changedFiles?.length ?? 0) > 4 && (
<span className="px-1 text-[10px] text-muted-foreground/55">
+{(workEntry.changedFiles?.length ?? 0) - 4}
Expand Down Expand Up @@ -1157,7 +1170,6 @@ const CollapsedWorkEntryGroup = memo(function CollapsedWorkEntryGroup(props: {
<p
key={`subentry:${entry.id}`}
className="truncate py-0.5 text-[10px] leading-4 text-muted-foreground/55"
title={preview ?? undefined}
>
{preview ?? heading}
</p>
Expand Down
Loading