-
-
-
-
- 1 && skills().length === group().length
+ }
+ fallback={
+ props.onToolOpenChange?.(tool().id, open)}
+ deferContent
+ virtualizeDiff={false}
+ onContentRendered={props.onSizeChange}
/>
-
-
- {(subtitle) => {subtitle()}}
-
-
- {(arg) => {arg}}
+ }
+ >
+ props.fileOpen?.(`${patchKeys().get(tool())}:${path}`))
+ }
+ onFileOpenChange={
+ props.onFileOpenChange &&
+ ((path, open) =>
+ props.onFileOpenChange?.(`${patchKeys().get(tool())}:${path}`, open))
+ }
+ onSizeChange={props.onSizeChange}
+ />
+
+ }
+ >
+ item.id)
+ .join(",")}
+ aria-label={i18n.plural("ui.tool.loadedSkills", skills().length, {
+ name: skills().join(", "),
+ })}
+ >
+
+ {loaded().split(marker)[0]?.trim()}
+
+
+
+ {(name, index) => (
+ <>
+ 0}>,
+
+ >
+ )}
-
-
- {(matches) => (
- <>
-
- {matches()}
- >
+
+
+ {(suffix) => (
+
+ {suffix()}
+
)}
+
+ }
+ >
+
+
+
+
+
+
+
+
+
+ {(subtitle) => {subtitle()}}
+
+
+ {(arg) => {arg}}
+
+
+
+ {(matches) => (
+ <>
+
+ {matches()}
+ >
+ )}
+
+
+
-
+
)
@@ -821,6 +844,94 @@ export function CurrentContextToolGroup(props: {
)
}
+export function CurrentReadToolGroup(props: {
+ tools: SessionMessageAssistantTool[]
+ open?: boolean
+ onOpenChange?: (open: boolean) => void
+ onSizeChange?: () => void
+}) {
+ const i18n = useI18n()
+ const [state, setState] = createStore({ open: false, truncated: false })
+ const open = () => props.open ?? state.open
+ const expandable = () => open() || state.truncated
+ const pending = createMemo(() =>
+ props.tools.some((tool) => tool.state.status === "streaming" || tool.state.status === "running"),
+ )
+ const files = createMemo(() =>
+ props.tools.flatMap((tool) => {
+ const input = currentToolInput(tool)
+ const name = getFilename(readToolPath(input) ?? "")
+ return name ? [{ id: tool.id, name, args: readArgs(input) }] : []
+ }),
+ )
+ let list: HTMLSpanElement | undefined
+ const measure = () => {
+ if (!list || open()) return
+ setState("truncated", list.scrollWidth > list.clientWidth)
+ }
+ createResizeObserver(() => list, measure)
+ createEffect(on(files, measure))
+ const toggle = () => {
+ if (!expandable()) return
+ const next = !open()
+ setState("open", next)
+ props.onOpenChange?.(next)
+ props.onSizeChange?.()
+ }
+
+ return (
+
tool.id).join(",")}
+ data-expanded={open() ? "true" : undefined}
+ role={expandable() ? "button" : undefined}
+ tabIndex={expandable() ? 0 : undefined}
+ aria-expanded={expandable() ? open() : undefined}
+ onClick={toggle}
+ onKeyDown={(event) => {
+ if (event.key !== "Enter" && event.key !== " ") return
+ event.preventDefault()
+ toggle()
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+ {(file, index) => (
+ <>
+ 0}>
+
+ {file().name}
+
+ {(arg) => {` ${arg}`}}
+
+ ,
+
+ >
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
export function CurrentFileToolGroup(props: {
tools: SessionMessageAssistantTool[]
fileOpen?: (path: string) => boolean | undefined
@@ -950,13 +1061,6 @@ function currentContextToolTrigger(tool: SessionMessageAssistantTool, i18n: Retu
typeof count === "number" && Number.isFinite(count) && count !== 0
? i18n.plural("ui.messagePart.context.match", count)
: undefined
- if (tool.name === "read") {
- const args = [
- ...(typeof input.offset === "number" ? [`offset=${input.offset}`] : []),
- ...(typeof input.limit === "number" ? [`limit=${input.limit}`] : []),
- ]
- return { title: i18n.t("ui.tool.read"), subtitle: getFilename(path), args, matches: undefined }
- }
if (tool.name === "list")
return { title: i18n.t("ui.tool.list"), subtitle: displayDirectory(path), args: [], matches: undefined }
if (tool.name === "glob")
@@ -1297,9 +1401,6 @@ ToolRegistry.register({
const data = useData()
const i18n = useI18n()
const image = createMemo(() => (props.status === "completed" ? readImagePath(props.input) : undefined))
- const args: string[] = []
- if (typeof props.input.offset === "number") args.push("offset=" + props.input.offset)
- if (typeof props.input.limit === "number") args.push("limit=" + props.input.limit)
const loaded = createMemo(() => {
if (props.status !== "completed") return []
const value = props.metadata.loaded
@@ -1328,7 +1429,7 @@ ToolRegistry.register({
trigger={{
title: i18n.t("ui.tool.read"),
subtitle: getFilename(readToolPath(props.input) ?? ""),
- args,
+ args: readArgs(props.input),
}}
>