Jay V 7 месяцев назад
Родитель
Сommit
7e4e6f6e51

+ 4 - 23
packages/web/src/components/Share.tsx

@@ -1,8 +1,6 @@
 import {
   For,
   Show,
-  Match,
-  Switch,
   onMount,
   Suspense,
   onCleanup,
@@ -12,13 +10,13 @@ import {
 } from "solid-js"
 import { DateTime } from "luxon"
 import { createStore, reconcile } from "solid-js/store"
-import { IconOpenAI, IconGemini, IconOpencode, IconAnthropic } from "./icons/custom"
-import { IconSparkles, IconArrowDown } from "./icons"
+import { IconArrowDown } from "./icons"
+import { IconOpencode } from "./icons/custom"
 import styles from "./share.module.css"
 import type { MessageV2 } from "opencode/session/message-v2"
 import type { Message } from "opencode/session/message"
 import type { Session } from "opencode/session/index"
-import { Part } from "./share/part"
+import { Part, ProviderIcon } from "./share/part"
 
 type Status = "disconnected" | "connecting" | "connected" | "error" | "reconnecting"
 
@@ -46,23 +44,6 @@ function getStatusText(status: [Status, string?]): string {
   }
 }
 
-function ProviderIcon(props: { provider: string; size?: number }) {
-  const size = props.size || 16
-  return (
-    <Switch fallback={<IconSparkles width={size} height={size} />}>
-      <Match when={props.provider === "openai"}>
-        <IconOpenAI width={size} height={size} />
-      </Match>
-      <Match when={props.provider === "anthropic"}>
-        <IconAnthropic width={size} height={size} />
-      </Match>
-      <Match when={props.provider === "gemini"}>
-        <IconGemini width={size} height={size} />
-      </Match>
-    </Switch>
-  )
-}
-
 export default function Share(props: {
   id: string
   api: string
@@ -319,7 +300,7 @@ export default function Share(props: {
                 {([provider, model]) => (
                   <li data-slot="item">
                     <div data-slot="icon" title={provider}>
-                      <ProviderIcon provider={provider} />
+                      <ProviderIcon model={model} />
                     </div>
                     <span data-slot="model">{model}</span>
                   </li>

+ 9 - 0
packages/web/src/components/icons/custom.tsx

@@ -49,3 +49,12 @@ export function IconOpencode(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
     </svg>
   )
 }
+
+// https://icones.js.org/collection/ri?s=meta&icon=ri:meta-fill
+export function IconMeta(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
+  return (
+    <svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+      <path fill="currentColor" d="M16.92 4.5c-1.851 0-3.298 1.394-4.608 3.165C10.512 5.373 9.007 4.5 7.206 4.5C3.534 4.5.72 9.28.72 14.338c0 3.165 1.531 5.162 4.096 5.162c1.846 0 3.174-.87 5.535-4.997c0 0 .984-1.737 1.66-2.934q.356.574.75 1.238l1.107 1.862c2.156 3.608 3.358 4.831 5.534 4.831c2.5 0 3.89-2.024 3.89-5.255c0-5.297-2.877-9.745-6.372-9.745m-8.37 8.886c-1.913 3-2.575 3.673-3.64 3.673c-1.097 0-1.749-.963-1.749-2.68c0-3.672 1.831-7.427 4.014-7.427c1.182 0 2.17.682 3.683 2.848c-1.437 2.204-2.307 3.586-2.307 3.586m7.224-.377L14.45 10.8a45 45 0 0 0-1.032-1.608c1.193-1.841 2.176-2.759 3.347-2.759c2.43 0 4.375 3.58 4.375 7.976c0 1.676-.549 2.649-1.686 2.649c-1.09 0-1.61-.72-3.68-4.05" />
+    </svg>
+  )
+}

+ 52 - 4
packages/web/src/components/share/part.tsx

@@ -1,20 +1,22 @@
-import { createMemo, createSignal, For, Match, Show, Switch, type JSX, type ParentProps } from "solid-js"
+import { For, Show, Match, Switch, type JSX, createMemo, createSignal, type ParentProps } from "solid-js"
 import {
-  IconCheckCircle,
-  IconChevronDown,
-  IconChevronRight,
   IconHashtag,
   IconSparkles,
   IconGlobeAlt,
   IconDocument,
   IconQueueList,
+  IconUserCircle,
   IconCommandLine,
+  IconCheckCircle,
+  IconChevronDown,
+  IconChevronRight,
   IconDocumentPlus,
   IconPencilSquare,
   IconRectangleStack,
   IconMagnifyingGlass,
   IconDocumentMagnifyingGlass,
 } from "../icons"
+import { IconMeta, IconOpenAI, IconGemini, IconAnthropic } from "../icons/custom"
 import styles from "./part.module.css"
 import type { MessageV2 } from "opencode/session/message-v2"
 import { ContentText } from "./content-text"
@@ -65,6 +67,15 @@ export function Part(props: PartProps) {
             }}
           >
             <Switch>
+              <Match when={props.message.role === "user" && props.part.type === "text"}>
+                <IconUserCircle width={18} height={18} />
+              </Match>
+              <Match when={props.message.role === "user" && props.part.type === "file"}>
+                <IconDocument 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>
               <Match when={props.part.type === "tool" && props.part.tool === "todowrite"}>
                 <IconQueueList width={18} height={18} />
               </Match>
@@ -130,6 +141,14 @@ export function Part(props: PartProps) {
             <Spacer />
           </>
         )}
+        {props.message.role === "user" && props.part.type === "file" && (
+          <div data-component="tool-title">
+            <span data-slot="name">Read</span>
+            <span data-slot="target" title={props.part.filename}>
+              {props.part.filename}
+            </span>
+          </div>
+        )}
         {props.part.type === "step-start" && props.message.role === "assistant" && (
           <div data-component="step-start">
             <div data-slot="provider">{props.message.providerID}</div>
@@ -662,3 +681,32 @@ function flattenToolArgs(obj: any, prefix: string = ""): Array<[string, any]> {
 
   return entries
 }
+
+function getProvider(model: string) {
+  const lowerModel = model.toLowerCase()
+
+  if (/claude|anthropic/.test(lowerModel)) return "anthropic"
+  if (/gpt|o[1-4]|codex|openai/.test(lowerModel)) return "openai"
+  if (/gemini|palm|bard|google/.test(lowerModel)) return "gemini"
+  if (/llama|meta/.test(lowerModel)) return "meta"
+
+  return "any"
+}
+
+export function ProviderIcon(props: { model: string; size?: number }) {
+  const provider = getProvider(props.model)
+  const size = props.size || 16
+  return (
+    <Switch fallback={<IconSparkles width={size} height={size} />}>
+      <Match when={provider === "openai"}>
+        <IconOpenAI width={size} height={size} />
+      </Match>
+      <Match when={provider === "anthropic"}>
+        <IconAnthropic width={size} height={size} />
+      </Match>
+      <Match when={provider === "gemini"}>
+        <IconGemini width={size} height={size} />
+      </Match>
+    </Switch>
+  )
+}