feat: tool timeout auto-retry, file tree attachment, force stop button

- Add configurable tool timeout with auto-retry: when a tool exceeds the
  timeout threshold, abort and send a follow-up message asking Claude to
  try a different approach
- Add "+" button on file tree items to attach files directly to chat
  input via new /api/files/raw endpoint and custom event bridge
- Add force stop button in streaming status bar when tool runs >90s,
  with warning indicators at 60s
- Improve doc preview markdown rendering (padding, list indentation)
- Bump version to 0.10.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
7418andClaude Opus 4.6 committed 2026-02-12 00:33:21 +08:00
1 parent 8747464c2f
commit 4fa26032a4
15 files changed
+314 -16

No files matched your search

+26 -4
View File
@@ -13,6 +13,7 @@ import {
FileIcon,
FolderIcon,
FolderOpenIcon,
PlusIcon,
} from "lucide-react";
import {
createContext,
@@ -27,6 +28,7 @@ interface FileTreeContextType {
togglePath: (path: string) => void;
selectedPath?: string;
onSelect?: (path: string) => void;
onAdd?: (path: string) => void;
}
// Default noop for context default value
@@ -44,6 +46,7 @@ export type FileTreeProps = HTMLAttributes<HTMLDivElement> & {
defaultExpanded?: Set<string>;
selectedPath?: string;
onSelect?: (path: string) => void;
onAdd?: (path: string) => void;
onExpandedChange?: (expanded: Set<string>) => void;
};
@@ -52,6 +55,7 @@ export const FileTree = ({
defaultExpanded = new Set(),
selectedPath,
onSelect,
onAdd,
onExpandedChange,
className,
children,
@@ -75,8 +79,8 @@ export const FileTree = ({
);
const contextValue = useMemo(
() => ({ expandedPaths, onSelect, selectedPath, togglePath }),
[expandedPaths, onSelect, selectedPath, togglePath]
() => ({ expandedPaths, onAdd, onSelect, selectedPath, togglePath }),
[expandedPaths, onAdd, onSelect, selectedPath, togglePath]
);
return (
@@ -204,7 +208,7 @@ export const FileTreeFile = ({
children,
...props
}: FileTreeFileProps) => {
const { selectedPath, onSelect } = useContext(FileTreeContext);
const { selectedPath, onSelect, onAdd } = useContext(FileTreeContext);
const isSelected = selectedPath === path;
const handleClick = useCallback(() => {
@@ -220,13 +224,21 @@ export const FileTreeFile = ({
[onSelect, path]
);
const handleAdd = useCallback(
(e: React.MouseEvent) => {
e.stopPropagation();
onAdd?.(path);
},
[onAdd, path]
);
const fileContextValue = useMemo(() => ({ name, path }), [name, path]);
return (
<FileTreeFileContext.Provider value={fileContextValue}>
<div
className={cn(
"flex cursor-pointer items-center gap-1 rounded px-2 py-1 transition-colors hover:bg-muted/50",
"group/file flex cursor-pointer items-center gap-1 rounded px-2 py-1 transition-colors hover:bg-muted/50",
isSelected && "bg-muted",
className
)}
@@ -244,6 +256,16 @@ export const FileTreeFile = ({
{icon ?? <FileIcon className="size-4 text-muted-foreground" />}
</FileTreeIcon>
<FileTreeName>{name}</FileTreeName>
{onAdd && (
<button
type="button"
className="ml-auto flex size-5 shrink-0 items-center justify-center rounded opacity-0 transition-opacity hover:bg-muted group-hover/file:opacity-100"
onClick={handleAdd}
title="Add to chat"
>
<PlusIcon className="size-3 text-muted-foreground" />
</button>
)}
</>
)}
</div>