summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--packages/web/src/components/Share.tsx169
-rw-r--r--packages/web/src/components/share.module.css34
2 files changed, 200 insertions, 3 deletions
diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx
index d1874afb6..15bc7fdc2 100644
--- a/packages/web/src/components/Share.tsx
+++ b/packages/web/src/components/Share.tsx
@@ -21,7 +21,9 @@ import {
IconCommandLine,
IconChevronRight,
IconPencilSquare,
+ IconRectangleStack,
IconWrenchScrewdriver,
+ IconDocumentArrowDown,
} from "./icons"
import DiffView from "./DiffView"
import CodeBlock from "./CodeBlock"
@@ -158,10 +160,12 @@ function ProviderIcon(props: { provider: string; size?: number }) {
}
interface ResultsButtonProps extends JSX.HTMLAttributes<HTMLButtonElement> {
+ showCopy?: string
+ hideCopy?: string
results: boolean
}
function ResultsButton(props: ResultsButtonProps) {
- const [local, rest] = splitProps(props, ["results"])
+ const [local, rest] = splitProps(props, ["results", "showCopy", "hideCopy"])
return (
<button
type="button"
@@ -169,7 +173,11 @@ function ResultsButton(props: ResultsButtonProps) {
data-element-button-more
{...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>
<Show
when={local.results}
@@ -726,6 +734,163 @@ export default function Share(props: { api: string }) {
</div>
)}
</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 */}
<Match
when={
diff --git a/packages/web/src/components/share.module.css b/packages/web/src/components/share.module.css
index f29343ed5..bedc1a3eb 100644
--- a/packages/web/src/components/share.module.css
+++ b/packages/web/src/components/share.module.css
@@ -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-tool-body] {
+ & > [data-section="content"] > [data-part-tool-body] {
gap: 0.5rem;
}
[data-part-title] {