mirror of
https://github.com/sst/opencode.git
synced 2025-08-30 09:47:25 +00:00
docs: share page task tool
This commit is contained in:
parent
73b46c2bf9
commit
749e7838a4
6 changed files with 86 additions and 39 deletions
|
@ -340,6 +340,7 @@ export default function Share(props: {
|
||||||
const filteredParts = createMemo(() =>
|
const filteredParts = createMemo(() =>
|
||||||
msg.parts.filter((x, index) => {
|
msg.parts.filter((x, index) => {
|
||||||
if (x.type === "step-start" && index > 0) return false
|
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 === "step-finish") return false
|
||||||
if (x.type === "text" && x.synthetic === true) return false
|
if (x.type === "text" && x.synthetic === true) return false
|
||||||
if (x.type === "tool" && x.tool === "todoread") return false
|
if (x.type === "tool" && x.tool === "todoread") return false
|
||||||
|
|
|
@ -58,3 +58,11 @@ export function IconMeta(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
</svg>
|
</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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -1,21 +1,13 @@
|
||||||
.root {
|
.root {
|
||||||
border: 1px solid var(--sl-color-blue-high);
|
|
||||||
padding: 0.5rem calc(0.5rem + 3px);
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
align-self: flex-start;
|
|
||||||
|
|
||||||
&[data-highlight="true"] {
|
|
||||||
background-color: var(--sl-color-blue-low);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-slot="expand-button"] {
|
[data-slot="expand-button"] {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
padding: 2px 0;
|
padding: 2px 0;
|
||||||
font-size: 0.75rem;
|
font-size: 0.857em;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-slot="markdown"] {
|
[data-slot="markdown"] {
|
||||||
|
@ -29,7 +21,7 @@
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
font-size: 0.875rem;
|
font-size: 1em;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
|
||||||
p,
|
p,
|
||||||
|
@ -61,7 +53,7 @@
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
font-size: 0.875rem;
|
font-size: 1em;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
@ -75,7 +67,7 @@
|
||||||
background-color: var(--sl-color-bg-surface) !important;
|
background-color: var(--sl-color-bg-surface) !important;
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.5rem 0.75rem;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
font-size: 0.75rem;
|
font-size: 0.857em;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
|
|
||||||
|
|
|
@ -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 { marked } from "marked"
|
||||||
import markedShiki from "marked-shiki"
|
|
||||||
import { codeToHtml } from "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(
|
const markedWithShiki = marked.use(
|
||||||
markedShiki({
|
markedShiki({
|
||||||
|
|
|
@ -103,7 +103,7 @@
|
||||||
[data-component="content"] {
|
[data-component="content"] {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
padding: 0 0 0.375rem;
|
padding: 0 0 1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
@ -135,6 +135,14 @@
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
max-width: var(--md-tool-width);
|
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"] {
|
[data-component="step-start"] {
|
||||||
|
@ -239,6 +247,24 @@
|
||||||
max-width: var(--lg-tool-width);
|
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"] {
|
[data-component="tool-title"] {
|
||||||
|
|
|
@ -19,25 +19,25 @@ import {
|
||||||
IconMagnifyingGlass,
|
IconMagnifyingGlass,
|
||||||
IconDocumentMagnifyingGlass,
|
IconDocumentMagnifyingGlass,
|
||||||
} from "../icons"
|
} from "../icons"
|
||||||
import { IconMeta, IconOpenAI, IconGemini, IconAnthropic } from "../icons/custom"
|
import { IconMeta, IconRobot, IconOpenAI, IconGemini, IconAnthropic } from "../icons/custom"
|
||||||
import { formatDuration } from "../share/common"
|
|
||||||
import { ContentCode } from "./content-code"
|
import { ContentCode } from "./content-code"
|
||||||
import { ContentDiff } from "./content-diff"
|
import { ContentDiff } from "./content-diff"
|
||||||
import { ContentText } from "./content-text"
|
import { ContentText } from "./content-text"
|
||||||
import { ContentError } from "./content-error"
|
|
||||||
import { ContentMarkdown } from "./content-markdown"
|
|
||||||
import { ContentBash } from "./content-bash"
|
import { ContentBash } from "./content-bash"
|
||||||
|
import { ContentError } from "./content-error"
|
||||||
|
import { formatDuration } from "../share/common"
|
||||||
|
import { ContentMarkdown } from "./content-markdown"
|
||||||
import type { MessageV2 } from "opencode/session/message-v2"
|
import type { MessageV2 } from "opencode/session/message-v2"
|
||||||
import type { Diagnostic } from "vscode-languageserver-types"
|
import type { Diagnostic } from "vscode-languageserver-types"
|
||||||
|
|
||||||
import styles from "./part.module.css"
|
import styles from "./part.module.css"
|
||||||
|
|
||||||
const MIN_DURATION = 2
|
const MIN_DURATION = 2000
|
||||||
|
|
||||||
export interface PartProps {
|
export interface PartProps {
|
||||||
index: number
|
index: number
|
||||||
message: MessageV2.Info
|
message: MessageV2.Info
|
||||||
part: MessageV2.AssistantPart | MessageV2.UserPart
|
part: MessageV2.Part
|
||||||
last: boolean
|
last: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ export function Part(props: PartProps) {
|
||||||
<IconGlobeAlt width={18} height={18} />
|
<IconGlobeAlt width={18} height={18} />
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={props.part.type === "tool" && props.part.tool === "task"}>
|
<Match when={props.part.type === "tool" && props.part.tool === "task"}>
|
||||||
<IconRectangleStack width={18} height={18} />
|
<IconRobot width={18} height={18} />
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={true}>
|
<Match when={true}>
|
||||||
<IconSparkles width={18} height={18} />
|
<IconSparkles width={18} height={18} />
|
||||||
|
@ -131,12 +131,13 @@ export function Part(props: PartProps) {
|
||||||
{props.message.role === "user" && props.part.type === "text" && (
|
{props.message.role === "user" && props.part.type === "text" && (
|
||||||
<div data-component="user-text">
|
<div data-component="user-text">
|
||||||
<ContentText text={props.part.text} expand={props.last} />
|
<ContentText text={props.part.text} expand={props.last} />
|
||||||
<Spacer />
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{props.message.role === "assistant" && props.part.type === "text" && (
|
{props.message.role === "assistant" && props.part.type === "text" && (
|
||||||
<div data-component="assistant-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 && (
|
{props.last && props.message.role === "assistant" && props.message.time.completed && (
|
||||||
<Footer
|
<Footer
|
||||||
title={DateTime.fromMillis(props.message.time.completed).toLocaleString(
|
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)}
|
{DateTime.fromMillis(props.message.time.completed).toLocaleString(DateTime.DATETIME_MED)}
|
||||||
</Footer>
|
</Footer>
|
||||||
)}
|
)}
|
||||||
<Spacer />
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{props.message.role === "user" && props.part.type === "file" && (
|
{props.message.role === "user" && props.part.type === "file" && (
|
||||||
|
@ -245,6 +245,14 @@ export function Part(props: PartProps) {
|
||||||
state={props.part.state}
|
state={props.part.state}
|
||||||
/>
|
/>
|
||||||
</Match>
|
</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}>
|
<Match when={true}>
|
||||||
<FallbackTool
|
<FallbackTool
|
||||||
message={props.message}
|
message={props.message}
|
||||||
|
@ -256,11 +264,10 @@ export function Part(props: PartProps) {
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
<ToolFooter
|
<ToolFooter
|
||||||
time={
|
time={DateTime.fromMillis(props.part.state.time.end)
|
||||||
DateTime.fromMillis(props.part.state.time.start)
|
.diff(DateTime.fromMillis(props.part.state.time.start))
|
||||||
.diff(DateTime.fromMillis(props.part.state.time.end))
|
.toMillis()}
|
||||||
.toMillis()
|
/>
|
||||||
} />
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -636,12 +643,25 @@ function Footer(props: ParentProps<{ title: string }>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function ToolFooter(props: { time: number }) {
|
function ToolFooter(props: { time: number }) {
|
||||||
return props.time > MIN_DURATION ? (
|
return props.time > MIN_DURATION && <Footer title={`${props.time}ms`}>{formatDuration(props.time)}</Footer>
|
||||||
<Footer title={`${props.time}ms`}>
|
}
|
||||||
{formatDuration(props.time)}
|
|
||||||
</Footer>
|
function TaskTool(props: ToolProps) {
|
||||||
) : (
|
return (
|
||||||
<Spacer />
|
<>
|
||||||
|
<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>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue