Browse Source

docs: share page fix terminal part

Jay V 7 months ago
parent
commit
c7f30e1065

+ 92 - 0
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;
+  }
+}

+ 67 - 0
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>
+  )
+}

+ 0 - 62
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;

+ 16 - 33
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}
+    />
   )
 }