diff options
| author | Jay V <[email protected]> | 2025-07-10 17:21:18 -0400 |
|---|---|---|
| committer | Jay V <[email protected]> | 2025-07-10 17:21:21 -0400 |
| commit | c7f30e1065c666f8eb687db75bdc06ce4b8c4882 (patch) | |
| tree | 8eb96f0f975d29fa17fb6033bc5eb2079ca56ab7 | |
| parent | 1c4fd7f28ff776953c8f3b191dc19243e6c6c8d1 (diff) | |
| download | opencode-c7f30e1065c666f8eb687db75bdc06ce4b8c4882.tar.gz opencode-c7f30e1065c666f8eb687db75bdc06ce4b8c4882.zip | |
docs: share page fix terminal part
| -rw-r--r-- | packages/web/src/components/share/content-bash.module.css | 92 | ||||
| -rw-r--r-- | packages/web/src/components/share/content-bash.tsx | 67 | ||||
| -rw-r--r-- | packages/web/src/components/share/part.module.css | 62 | ||||
| -rw-r--r-- | packages/web/src/components/share/part.tsx | 49 |
4 files changed, 175 insertions, 95 deletions
diff --git a/packages/web/src/components/share/content-bash.module.css b/packages/web/src/components/share/content-bash.module.css new file mode 100644 index 000000000..0d7453156 --- /dev/null +++ b/packages/web/src/components/share/content-bash.module.css @@ -0,0 +1,92 @@ +.root { + width: 100%; + max-width: var(--sm-tool-width); + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 0.5rem; + + [data-slot="expand-button"] { + flex: 0 0 auto; + padding: 2px 0; + font-size: 0.75rem; + } + + [data-slot="body"] { + display: flex; + flex-direction: column; + border: 1px solid var(--sl-color-divider); + border-radius: 0.25rem; + overflow: hidden; + width: 100%; + } + + [data-slot="header"] { + position: relative; + border-bottom: 1px solid var(--sl-color-divider); + width: 100%; + height: 1.625rem; + text-align: center; + padding: 0 3.25rem; + + > span { + max-width: min(100%, 140ch); + display: inline-block; + white-space: nowrap; + overflow: hidden; + line-height: 1.625rem; + font-size: 0.75rem; + text-overflow: ellipsis; + color: var(--sl-color-text-dimmed); + } + + &::before { + content: ""; + position: absolute; + pointer-events: none; + top: 8px; + left: 10px; + width: 2rem; + height: 0.5rem; + line-height: 0; + background-color: var(--sl-color-hairline); + mask-image: var(--term-icon); + mask-repeat: no-repeat; + } + } + + [data-slot="content"] { + display: flex; + flex-direction: column; + padding: 0.5rem calc(0.5rem + 3px); + + pre { + --shiki-dark-bg: var(--sl-color-bg) !important; + background-color: var(--sl-color-bg) !important; + line-height: 1.6; + font-size: 0.75rem; + white-space: pre-wrap; + word-break: break-word; + margin: 0; + + span { + white-space: break-spaces; + } + } + } + + [data-slot="output"] { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 10; + line-clamp: 10; + overflow: hidden; + } + + &[data-expanded] [data-slot="output"] { + display: block; + -webkit-line-clamp: none; + line-clamp: none; + overflow: visible; + } +} diff --git a/packages/web/src/components/share/content-bash.tsx b/packages/web/src/components/share/content-bash.tsx new file mode 100644 index 000000000..5ccd95c0b --- /dev/null +++ b/packages/web/src/components/share/content-bash.tsx @@ -0,0 +1,67 @@ +import style from "./content-bash.module.css" +import { createResource, createSignal } from "solid-js" +import { createOverflow } from "./common" +import { codeToHtml } from "shiki" + +interface Props { + command: string + output: string + description?: string + expand?: boolean +} + +export function ContentBash(props: Props) { + const [commandHtml] = createResource( + () => props.command, + async (command) => { + return codeToHtml(command || "", { + lang: "bash", + themes: { + light: "github-light", + dark: "github-dark", + }, + }) + }, + ) + + const [outputHtml] = createResource( + () => props.output, + async (output) => { + return codeToHtml(output || "", { + lang: "console", + themes: { + light: "github-light", + dark: "github-dark", + }, + }) + }, + ) + + const [expanded, setExpanded] = createSignal(false) + const overflow = createOverflow() + + return ( + <div class={style.root} data-expanded={expanded() || props.expand === true ? true : undefined}> + <div data-slot="body"> + <div data-slot="header"> + <span>{props.description}</span> + </div> + <div data-slot="content"> + <div innerHTML={commandHtml()} /> + <div data-slot="output" ref={overflow.ref} innerHTML={outputHtml()} /> + </div> + </div> + + {!props.expand && overflow.status && ( + <button + type="button" + data-component="text-button" + data-slot="expand-button" + onClick={() => setExpanded((e) => !e)} + > + {expanded() ? "Show less" : "Show more"} + </button> + )} + </div> + ) +} diff --git a/packages/web/src/components/share/part.module.css b/packages/web/src/components/share/part.module.css index a11221ea1..bf843bc0d 100644 --- a/packages/web/src/components/share/part.module.css +++ b/packages/web/src/components/share/part.module.css @@ -307,68 +307,6 @@ } } - [data-component="terminal"] { - width: 100%; - max-width: var(--sm-tool-width); - - [data-slot="body"] { - display: flex; - flex-direction: column; - border: 1px solid var(--sl-color-divider); - border-radius: 0.25rem; - overflow: hidden; - } - - [data-slot="header"] { - position: relative; - border-bottom: 1px solid var(--sl-color-divider); - width: 100%; - height: 1.625rem; - text-align: center; - padding: 0 3.25rem; - - > span { - max-width: min(100%, 140ch); - display: inline-block; - white-space: nowrap; - overflow: hidden; - line-height: 1.625rem; - font-size: 0.75rem; - text-overflow: ellipsis; - color: var(--sl-color-text-dimmed); - } - - &::before { - content: ""; - position: absolute; - pointer-events: none; - top: 8px; - left: 10px; - width: 2rem; - height: 0.5rem; - line-height: 0; - background-color: var(--sl-color-hairline); - mask-image: var(--term-icon); - mask-repeat: no-repeat; - } - } - - [data-slot="content"] { - display: flex; - flex-direction: column; - padding: 0.5rem calc(0.5rem + 3px); - - pre { - --shiki-dark-bg: var(--sl-color-bg) !important; - background-color: var(--sl-color-bg) !important; - line-height: 1.6; - font-size: 0.75rem; - white-space: pre-wrap; - word-break: break-word; - } - } - } - [data-component="tool-args"] { display: inline-grid; align-items: center; diff --git a/packages/web/src/components/share/part.tsx b/packages/web/src/components/share/part.tsx index 7a52454d9..fbc1ae16d 100644 --- a/packages/web/src/components/share/part.tsx +++ b/packages/web/src/components/share/part.tsx @@ -1,15 +1,6 @@ import map from "lang-map" import { DateTime } from "luxon" -import { - For, - Show, - Match, - Switch, - type JSX, - createMemo, - createSignal, - type ParentProps -} from "solid-js" +import { For, Show, Match, Switch, type JSX, createMemo, createSignal, type ParentProps } from "solid-js" import { IconHashtag, IconSparkles, @@ -34,6 +25,7 @@ import { ContentDiff } from "./content-diff" import { ContentText } from "./content-text" import { ContentError } from "./content-error" 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" @@ -83,8 +75,10 @@ export function Part(props: PartProps) { <Match when={props.message.role === "user" && props.part.type === "file"}> <IconPaperClip width={18} height={18} /> </Match> - <Match when={props.part.type === "step-start" && props.message.role === "assistant" && props.message.modelID}> - {model => <ProviderIcon model={model()} size={18} />} + <Match + when={props.part.type === "step-start" && props.message.role === "assistant" && props.message.modelID} + > + {(model) => <ProviderIcon model={model()} size={18} />} </Match> <Match when={props.part.type === "tool" && props.part.tool === "todowrite"}> <IconQueueList width={18} height={18} /> @@ -164,14 +158,11 @@ export function Part(props: PartProps) { <div data-slot="model">{props.message.modelID}</div> </div> )} - {props.part.type === "tool" && - props.part.state.status === "error" && ( - <div data-component="tool"> - <ContentError> - {formatErrorString(props.part.state.error)} - </ContentError> - </div> - )} + {props.part.type === "tool" && props.part.state.status === "error" && ( + <div data-component="tool"> + <ContentError>{formatErrorString(props.part.state.error)}</ContentError> + </div> + )} {props.part.type === "tool" && props.part.state.status === "completed" && props.message.role === "assistant" && ( @@ -565,19 +556,11 @@ export function EditTool(props: ToolProps) { export function BashTool(props: ToolProps) { return ( - <> - <div data-component="terminal" data-size="sm"> - <div data-slot="body"> - <div data-slot="header"> - <span>{props.state.metadata.description}</span> - </div> - <div data-slot="content"> - <ContentCode flush lang="bash" code={props.state.input.command} /> - <ContentCode flush lang="console" code={props.state.metadata?.stdout || ""} /> - </div> - </div> - </div> - </> + <ContentBash + command={props.state.input.command} + output={props.state.metadata?.stdout || ""} + description={props.state.metadata.description} + /> ) } |
