mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-21 17:54:23 +01:00
read tool share page
This commit is contained in:
@@ -21,7 +21,9 @@ import {
|
|||||||
IconCommandLine,
|
IconCommandLine,
|
||||||
IconChevronRight,
|
IconChevronRight,
|
||||||
IconPencilSquare,
|
IconPencilSquare,
|
||||||
|
IconRectangleStack,
|
||||||
IconWrenchScrewdriver,
|
IconWrenchScrewdriver,
|
||||||
|
IconDocumentArrowDown,
|
||||||
} from "./icons"
|
} from "./icons"
|
||||||
import DiffView from "./DiffView"
|
import DiffView from "./DiffView"
|
||||||
import CodeBlock from "./CodeBlock"
|
import CodeBlock from "./CodeBlock"
|
||||||
@@ -158,10 +160,12 @@ function ProviderIcon(props: { provider: string; size?: number }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ResultsButtonProps extends JSX.HTMLAttributes<HTMLButtonElement> {
|
interface ResultsButtonProps extends JSX.HTMLAttributes<HTMLButtonElement> {
|
||||||
|
showCopy?: string
|
||||||
|
hideCopy?: string
|
||||||
results: boolean
|
results: boolean
|
||||||
}
|
}
|
||||||
function ResultsButton(props: ResultsButtonProps) {
|
function ResultsButton(props: ResultsButtonProps) {
|
||||||
const [local, rest] = splitProps(props, ["results"])
|
const [local, rest] = splitProps(props, ["results", "showCopy", "hideCopy"])
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -169,7 +173,11 @@ function ResultsButton(props: ResultsButtonProps) {
|
|||||||
data-element-button-more
|
data-element-button-more
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
<span>{local.results ? "Hide results" : "Show results"}</span>
|
<span>
|
||||||
|
{local.results
|
||||||
|
? local.hideCopy || "Hide results"
|
||||||
|
: local.showCopy || "Show results"}
|
||||||
|
</span>
|
||||||
<span data-button-icon>
|
<span data-button-icon>
|
||||||
<Show
|
<Show
|
||||||
when={local.results}
|
when={local.results}
|
||||||
@@ -726,6 +734,163 @@ export default function Share(props: { api: string }) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Match>
|
</Match>
|
||||||
|
{/* LS tool */}
|
||||||
|
<Match
|
||||||
|
when={
|
||||||
|
msg.role === "assistant" &&
|
||||||
|
part.type === "tool-invocation" &&
|
||||||
|
part.toolInvocation.toolName === "opencode_list" &&
|
||||||
|
part
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{(part) => {
|
||||||
|
const metadata = createMemo(() =>
|
||||||
|
msg.metadata?.tool[part().toolInvocation.toolCallId]
|
||||||
|
)
|
||||||
|
const args = part().toolInvocation.args
|
||||||
|
const path = args.path
|
||||||
|
|
||||||
|
const duration = createMemo(() =>
|
||||||
|
DateTime.fromMillis(metadata()?.time.end || 0).diff(
|
||||||
|
DateTime.fromMillis(metadata()?.time.start || 0),
|
||||||
|
).toMillis(),
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-section="part" data-part-type="tool-list">
|
||||||
|
<div data-section="decoration">
|
||||||
|
<div title="List files">
|
||||||
|
<IconRectangleStack width={18} height={18} />
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
<div data-section="content">
|
||||||
|
<div data-part-tool-body>
|
||||||
|
<span data-part-title data-size="md">
|
||||||
|
<span data-element-label>LS</span>
|
||||||
|
<b>{path}</b>
|
||||||
|
</span>
|
||||||
|
<Switch>
|
||||||
|
<Match
|
||||||
|
when={
|
||||||
|
part().toolInvocation.state ===
|
||||||
|
"result" &&
|
||||||
|
part().toolInvocation.result
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div data-part-tool-result>
|
||||||
|
<ResultsButton
|
||||||
|
results={results()}
|
||||||
|
onClick={() => showResults((e) => !e)}
|
||||||
|
/>
|
||||||
|
<Show when={results()}>
|
||||||
|
<TextPart
|
||||||
|
expand
|
||||||
|
data-size="sm"
|
||||||
|
data-color="dimmed"
|
||||||
|
text={part().toolInvocation.result}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
<ToolFooter time={duration()} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</Match>
|
||||||
|
{/* Read tool */}
|
||||||
|
<Match
|
||||||
|
when={
|
||||||
|
msg.role === "assistant" &&
|
||||||
|
part.type === "tool-invocation" &&
|
||||||
|
part.toolInvocation.toolName === "opencode_read" &&
|
||||||
|
part
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{(part) => {
|
||||||
|
const metadata = createMemo(() => msg.metadata?.tool[part().toolInvocation.toolCallId])
|
||||||
|
const args = part().toolInvocation.args
|
||||||
|
const filePath = args.filePath
|
||||||
|
const hasError = metadata()?.error
|
||||||
|
const preview = metadata()?.preview
|
||||||
|
const result = part().toolInvocation.state === "result" && part().toolInvocation.result
|
||||||
|
|
||||||
|
const duration = createMemo(() =>
|
||||||
|
DateTime.fromMillis(metadata()?.time.end || 0).diff(
|
||||||
|
DateTime.fromMillis(metadata()?.time.start || 0),
|
||||||
|
).toMillis(),
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-section="part" data-part-type="tool-read">
|
||||||
|
<div data-section="decoration">
|
||||||
|
<div title="Read file">
|
||||||
|
<IconDocumentArrowDown width={18} height={18} />
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
<div data-section="content">
|
||||||
|
<div data-part-tool-body>
|
||||||
|
<span data-part-title data-size="md">
|
||||||
|
<span data-element-label>Read</span>
|
||||||
|
<b>{filePath}</b>
|
||||||
|
</span>
|
||||||
|
<Switch>
|
||||||
|
<Match when={hasError}>
|
||||||
|
<div data-part-tool-result>
|
||||||
|
<TextPart
|
||||||
|
expand
|
||||||
|
text={result}
|
||||||
|
data-size="sm"
|
||||||
|
data-color="dimmed"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Match>
|
||||||
|
<Match when={preview}>
|
||||||
|
<div data-part-tool-result>
|
||||||
|
<ResultsButton
|
||||||
|
showCopy="Show preview"
|
||||||
|
hideCopy="Hide preview"
|
||||||
|
results={results()}
|
||||||
|
onClick={() => showResults((e) => !e)}
|
||||||
|
/>
|
||||||
|
<Show when={results()}>
|
||||||
|
<div data-part-tool-code>
|
||||||
|
<CodeBlock
|
||||||
|
lang={getFileType(filePath)}
|
||||||
|
code={preview}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</Match>
|
||||||
|
<Match when={result}>
|
||||||
|
<div data-part-tool-result>
|
||||||
|
<ResultsButton
|
||||||
|
results={results()}
|
||||||
|
onClick={() => showResults((e) => !e)}
|
||||||
|
/>
|
||||||
|
<Show when={results()}>
|
||||||
|
<TextPart
|
||||||
|
expand
|
||||||
|
text={result}
|
||||||
|
data-size="sm"
|
||||||
|
data-color="dimmed"
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
<ToolFooter time={duration()} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</Match>
|
||||||
{/* Edit tool */}
|
{/* Edit tool */}
|
||||||
<Match
|
<Match
|
||||||
when={
|
when={
|
||||||
|
|||||||
@@ -314,8 +314,40 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-part-type="tool-list"],
|
||||||
|
[data-part-type="tool-read"] {
|
||||||
|
& > [data-section="content"] > [data-part-tool-body] {
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
[data-part-title] {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.5rem;
|
||||||
|
|
||||||
|
b {
|
||||||
|
color: var(--sl-color-text);
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-part-type="tool-read"] {
|
||||||
|
[data-part-tool-result] {
|
||||||
|
[data-part-tool-code] {
|
||||||
|
border: 1px solid var(--sl-color-divider);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
padding: 0.5rem calc(0.5rem + 3px);
|
||||||
|
|
||||||
|
pre {
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[data-part-type="tool-edit"] {
|
[data-part-type="tool-edit"] {
|
||||||
[data-part-tool-body] {
|
& > [data-section="content"] > [data-part-tool-body] {
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
[data-part-title] {
|
[data-part-title] {
|
||||||
|
|||||||
Reference in New Issue
Block a user