diff options
| author | Jay V <[email protected]> | 2025-07-15 17:12:09 -0400 |
|---|---|---|
| committer | Jay V <[email protected]> | 2025-07-15 17:47:22 -0400 |
| commit | 749e7838a444d6f2f846bd7e6e008edae25b0c69 (patch) | |
| tree | 2600d24c2bf539893da6d9977e44293e835d9294 /packages/web/src/components | |
| parent | 73b46c2bf9090094a5e31db62ef16fe1b08bb01e (diff) | |
| download | opencode-749e7838a444d6f2f846bd7e6e008edae25b0c69.tar.gz opencode-749e7838a444d6f2f846bd7e6e008edae25b0c69.zip | |
docs: share page task tool
Diffstat (limited to 'packages/web/src/components')
| -rw-r--r-- | packages/web/src/components/Share.tsx | 1 | ||||
| -rw-r--r-- | packages/web/src/components/icons/custom.tsx | 8 | ||||
| -rw-r--r-- | packages/web/src/components/share/content-markdown.module.css | 16 | ||||
| -rw-r--r-- | packages/web/src/components/share/content-markdown.tsx | 10 | ||||
| -rw-r--r-- | packages/web/src/components/share/part.module.css | 28 | ||||
| -rw-r--r-- | packages/web/src/components/share/part.tsx | 60 |
6 files changed, 85 insertions, 38 deletions
diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx index d5eebd45d..c75b5afb6 100644 --- a/packages/web/src/components/Share.tsx +++ b/packages/web/src/components/Share.tsx @@ -340,6 +340,7 @@ export default function Share(props: { const filteredParts = createMemo(() => msg.parts.filter((x, index) => { if (x.type === "step-start" && index > 0) return false + if (x.type === "snapshot") return false if (x.type === "step-finish") return false if (x.type === "text" && x.synthetic === true) return false if (x.type === "tool" && x.tool === "todoread") return false diff --git a/packages/web/src/components/icons/custom.tsx b/packages/web/src/components/icons/custom.tsx index 95f64c191..ba06ddfb3 100644 --- a/packages/web/src/components/icons/custom.tsx +++ b/packages/web/src/components/icons/custom.tsx @@ -58,3 +58,11 @@ export function IconMeta(props: JSX.SvgSVGAttributes<SVGSVGElement>) { </svg> ) } + +// https://icones.js.org/collection/ri?s=robot&icon=ri:robot-2-line +export function IconRobot(props: JSX.SvgSVGAttributes<SVGSVGElement>) { + return ( + <svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <path fill="currentColor" d="M13.5 2c0 .444-.193.843-.5 1.118V5h5a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V8a3 3 0 0 1 3-3h5V3.118A1.5 1.5 0 1 1 13.5 2M6 7a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V8a1 1 0 0 0-1-1zm-4 3H0v6h2zm20 0h2v6h-2zM9 14.5a1.5 1.5 0 1 0 0-3a1.5 1.5 0 0 0 0 3m6 0a1.5 1.5 0 1 0 0-3a1.5 1.5 0 0 0 0 3" /></svg> + ) +} diff --git a/packages/web/src/components/share/content-markdown.module.css b/packages/web/src/components/share/content-markdown.module.css index 0e6b83620..3e38ddf02 100644 --- a/packages/web/src/components/share/content-markdown.module.css +++ b/packages/web/src/components/share/content-markdown.module.css @@ -1,21 +1,13 @@ .root { - border: 1px solid var(--sl-color-blue-high); - padding: 0.5rem calc(0.5rem + 3px); - border-radius: 0.25rem; display: flex; flex-direction: column; align-items: flex-start; gap: 1rem; - align-self: flex-start; - - &[data-highlight="true"] { - background-color: var(--sl-color-blue-low); - } [data-slot="expand-button"] { flex: 0 0 auto; padding: 2px 0; - font-size: 0.75rem; + font-size: 0.857em; } [data-slot="markdown"] { @@ -29,7 +21,7 @@ display: block; } - font-size: 0.875rem; + font-size: 1em; line-height: 1.5; p, @@ -61,7 +53,7 @@ h4, h5, h6 { - font-size: 0.875rem; + font-size: 1em; font-weight: 600; margin-bottom: 0.5rem; } @@ -75,7 +67,7 @@ background-color: var(--sl-color-bg-surface) !important; padding: 0.5rem 0.75rem; line-height: 1.6; - font-size: 0.75rem; + font-size: 0.857em; white-space: pre-wrap; word-break: break-word; diff --git a/packages/web/src/components/share/content-markdown.tsx b/packages/web/src/components/share/content-markdown.tsx index f79271296..a083872e7 100644 --- a/packages/web/src/components/share/content-markdown.tsx +++ b/packages/web/src/components/share/content-markdown.tsx @@ -1,10 +1,10 @@ -import style from "./content-markdown.module.css" -import { createResource, createSignal } from "solid-js" -import { createOverflow } from "./common" -import { transformerNotationDiff } from "@shikijs/transformers" import { marked } from "marked" -import markedShiki from "marked-shiki" import { codeToHtml } from "shiki" +import markedShiki from "marked-shiki" +import { createOverflow } from "./common" +import { createResource, createSignal } from "solid-js" +import { transformerNotationDiff } from "@shikijs/transformers" +import style from "./content-markdown.module.css" const markedWithShiki = marked.use( markedShiki({ diff --git a/packages/web/src/components/share/part.module.css b/packages/web/src/components/share/part.module.css index 5ffb83f6b..17018fa02 100644 --- a/packages/web/src/components/share/part.module.css +++ b/packages/web/src/components/share/part.module.css @@ -103,7 +103,7 @@ [data-component="content"] { flex: 1 1 auto; min-width: 0; - padding: 0 0 0.375rem; + padding: 0 0 1rem; display: flex; flex-direction: column; gap: 1rem; @@ -135,6 +135,14 @@ gap: 1rem; flex-grow: 1; max-width: var(--md-tool-width); + + & > [data-component="assistant-text-markdown"] { + align-self: flex-start; + font-size: 0.875rem; + border: 1px solid var(--sl-color-blue-high); + padding: 0.5rem calc(0.5rem + 3px); + border-radius: 0.25rem; + } } [data-component="step-start"] { @@ -239,6 +247,24 @@ max-width: var(--lg-tool-width); } } + &[data-tool="task"] { + [data-component="tool-input"] { + font-size: 0.75rem; + line-height: 1.5; + max-width: var(--sm-tool-width); + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + } + [data-component="tool-output"] { + max-width: var(--sm-tool-width); + font-size: 0.75rem; + border: 1px solid var(--sl-color-divider); + padding: 0.5rem calc(0.5rem + 3px); + border-radius: 0.25rem; + } + } } [data-component="tool-title"] { diff --git a/packages/web/src/components/share/part.tsx b/packages/web/src/components/share/part.tsx index fcfa8bec4..4a9320e6d 100644 --- a/packages/web/src/components/share/part.tsx +++ b/packages/web/src/components/share/part.tsx @@ -19,25 +19,25 @@ import { IconMagnifyingGlass, IconDocumentMagnifyingGlass, } from "../icons" -import { IconMeta, IconOpenAI, IconGemini, IconAnthropic } from "../icons/custom" -import { formatDuration } from "../share/common" +import { IconMeta, IconRobot, IconOpenAI, IconGemini, IconAnthropic } from "../icons/custom" import { ContentCode } from "./content-code" import { ContentDiff } from "./content-diff" import { ContentText } from "./content-text" +import { ContentBash } from "./content-bash" import { ContentError } from "./content-error" +import { formatDuration } from "../share/common" import { ContentMarkdown } from "./content-markdown" -import { ContentBash } from "./content-bash" import type { MessageV2 } from "opencode/session/message-v2" import type { Diagnostic } from "vscode-languageserver-types" import styles from "./part.module.css" -const MIN_DURATION = 2 +const MIN_DURATION = 2000 export interface PartProps { index: number message: MessageV2.Info - part: MessageV2.AssistantPart | MessageV2.UserPart + part: MessageV2.Part last: boolean } @@ -114,7 +114,7 @@ export function Part(props: PartProps) { <IconGlobeAlt width={18} height={18} /> </Match> <Match when={props.part.type === "tool" && props.part.tool === "task"}> - <IconRectangleStack width={18} height={18} /> + <IconRobot width={18} height={18} /> </Match> <Match when={true}> <IconSparkles width={18} height={18} /> @@ -131,12 +131,13 @@ export function Part(props: PartProps) { {props.message.role === "user" && props.part.type === "text" && ( <div data-component="user-text"> <ContentText text={props.part.text} expand={props.last} /> - <Spacer /> </div> )} {props.message.role === "assistant" && props.part.type === "text" && ( <div data-component="assistant-text"> - <ContentMarkdown expand={props.last} text={props.part.text} /> + <div data-component="assistant-text-markdown"> + <ContentMarkdown expand={props.last} text={props.part.text} /> + </div> {props.last && props.message.role === "assistant" && props.message.time.completed && ( <Footer title={DateTime.fromMillis(props.message.time.completed).toLocaleString( @@ -146,7 +147,6 @@ export function Part(props: PartProps) { {DateTime.fromMillis(props.message.time.completed).toLocaleString(DateTime.DATETIME_MED)} </Footer> )} - <Spacer /> </div> )} {props.message.role === "user" && props.part.type === "file" && ( @@ -245,6 +245,14 @@ export function Part(props: PartProps) { state={props.part.state} /> </Match> + <Match when={props.part.tool === "task"}> + <TaskTool + id={props.part.id} + tool={props.part.tool} + message={props.message} + state={props.part.state} + /> + </Match> <Match when={true}> <FallbackTool message={props.message} @@ -256,11 +264,10 @@ export function Part(props: PartProps) { </Switch> </div> <ToolFooter - time={ - DateTime.fromMillis(props.part.state.time.start) - .diff(DateTime.fromMillis(props.part.state.time.end)) - .toMillis() - } /> + time={DateTime.fromMillis(props.part.state.time.end) + .diff(DateTime.fromMillis(props.part.state.time.start)) + .toMillis()} + /> </> )} </div> @@ -636,12 +643,25 @@ function Footer(props: ParentProps<{ title: string }>) { } function ToolFooter(props: { time: number }) { - return props.time > MIN_DURATION ? ( - <Footer title={`${props.time}ms`}> - {formatDuration(props.time)} - </Footer> - ) : ( - <Spacer /> + return props.time > MIN_DURATION && <Footer title={`${props.time}ms`}>{formatDuration(props.time)}</Footer> +} + +function TaskTool(props: ToolProps) { + return ( + <> + <div data-component="tool-title"> + <span data-slot="name">Task</span> + <span data-slot="target">{props.state.input.description}</span> + </div> + <div data-component="tool-input"> + “{props.state.input.prompt}” + </div> + <ResultsButton showCopy="Show output" hideCopy="Hide output"> + <div data-component="tool-output"> + <ContentMarkdown expand text={props.state.output} /> + </div> + </ResultsButton> + </> ) } |
