Jelajahi Sumber

tui: show model context in run footer (#30380)

Simon Klee 3 bulan lalu
induk
melakukan
8936914e9d

+ 4 - 0
packages/opencode/src/cli/cmd/run/footer.ts

@@ -739,7 +739,11 @@ export class RunFooter implements FooterApi {
       return
     }
 
+    const previous = this.currentModel()
     this.setCurrentModel(model)
+    if (!previous || previous.providerID !== model.providerID || previous.modelID !== model.modelID) {
+      this.setCurrentVariant(undefined)
+    }
     void Promise.resolve()
       .then(() => this.options.onModelSelect?.(model))
       .then((result) => {

+ 37 - 23
packages/opencode/src/cli/cmd/run/footer.view.tsx

@@ -53,6 +53,7 @@ import type {
   RunTuiConfig,
 } from "./types"
 import { RUN_THEME_FALLBACK, type RunTheme } from "./theme"
+import { modelInfo } from "./variant.shared"
 
 const EMPTY_BORDER = {
   topLeft: "",
@@ -160,7 +161,11 @@ export function RunFooterView(props: RunFooterViewProps) {
   const queuedIndicator = createMemo(() => {
     const count = queuedPrompts().length
     if (count === 0) return
-    return { count, label: count === 1 ? "prompt" : "prompts" }
+    return { count }
+  })
+  const model = createMemo(() => {
+    const current = props.currentModel()
+    return current ? modelInfo(props.providers(), current) : { model: props.state().model, provider: undefined }
   })
   const detail = createMemo(() => {
     const current = route()
@@ -617,16 +622,33 @@ export function RunFooterView(props: RunFooterViewProps) {
                       {shell() ? "Shell" : props.agent}
                     </text>
                     <Show when={!shell()}>
-                      <text
-                        id="run-direct-footer-model"
-                        fg={theme().text}
-                        wrapMode="none"
-                        truncate
-                        flexGrow={1}
-                        flexShrink={1}
-                      >
-                        {props.state().model}
-                      </text>
+                      <box id="run-direct-footer-model" flexDirection="row" gap={1} flexGrow={1} flexShrink={1}>
+                        <text fg={theme().muted} wrapMode="none" flexShrink={0}>
+                          ·
+                        </text>
+                        <text fg={theme().text} wrapMode="none" truncate flexShrink={1}>
+                          {model().model}
+                        </text>
+                        <Show when={model().provider}>
+                          {(provider) => (
+                            <text fg={theme().muted} wrapMode="none" truncate flexShrink={1}>
+                              {provider()}
+                            </text>
+                          )}
+                        </Show>
+                        <Show when={props.currentVariant()}>
+                          {(variant) => (
+                            <>
+                              <text fg={theme().muted} wrapMode="none" flexShrink={0}>
+                                ·
+                              </text>
+                              <text wrapMode="none" truncate flexShrink={1}>
+                                <span style={{ fg: theme().warning, bold: true }}>{variant()}</span>
+                              </text>
+                            </>
+                          )}
+                        </Show>
+                      </box>
                     </Show>
                   </box>
                 </Show>
@@ -746,26 +768,18 @@ export function RunFooterView(props: RunFooterViewProps) {
                         <Show when={subagentIndicator()}>
                           {(info) => (
                             <text id="run-direct-footer-subagents-label" fg={theme().text} wrapMode="none" truncate>
-                              <Show when={busy() || exiting() || duration().length > 0}>
-                                <span style={{ fg: theme().muted }}>· </span>
-                              </Show>
-                              {info().count} <span style={{ fg: theme().muted }}>{info().label}</span>
-                              <span style={{ fg: theme().muted }}> · </span>
+                              <span style={{ fg: theme().highlight }}>• </span>
+                              {info().count} <span style={{ fg: theme().muted }}>{info().label} </span>
                               <span style={{ fg: theme().highlight }}>{subagentShortcut() || "leader+down"}</span>
-                              <span style={{ fg: theme().muted }}> to view</span>
                             </text>
                           )}
                         </Show>
                         <Show when={queuedIndicator()}>
                           {(info) => (
                             <text id="run-direct-footer-queued-label" fg={theme().text} wrapMode="none" truncate>
-                              <Show when={busy() || exiting() || duration().length > 0 || subagentIndicator()}>
-                                <span style={{ fg: theme().muted }}>· </span>
-                              </Show>
-                              {info().count} <span style={{ fg: theme().muted }}>queued {info().label}</span>
-                              <span style={{ fg: theme().muted }}> · </span>
+                              <span style={{ fg: theme().warning }}>• </span>
+                              {info().count} <span style={{ fg: theme().muted }}>queued </span>
                               <span style={{ fg: theme().highlight }}>{queuedShortcut() || "leader+q"}</span>
-                              <span style={{ fg: theme().muted }}> to edit/remove</span>
                             </text>
                           )}
                         </Show>

+ 1 - 1
packages/opencode/src/cli/cmd/run/variant.shared.ts

@@ -39,7 +39,7 @@ function variantKey(model: NonNullable<RunInput["model"]>): string {
   return modelKey(model.providerID, model.modelID)
 }
 
-function modelInfo(providers: RunProvider[] | undefined, model: NonNullable<RunInput["model"]>) {
+export function modelInfo(providers: RunProvider[] | undefined, model: NonNullable<RunInput["model"]>) {
   const provider = providers?.find((item) => item.id === model.providerID)
   return {
     provider: provider?.name ?? model.providerID,

+ 3 - 2
packages/opencode/test/cli/run/footer.view.test.tsx

@@ -647,9 +647,10 @@ test("direct footer shows editable prompts and additional queued work while runn
 
   try {
     await app.renderOnce()
-    expect(app.captureCharFrame()).toContain("interrupt · 1 agent · ctrl+x down to view · 1 queued prompt · ctrl+x q")
+    expect(app.captureCharFrame()).toContain("interrupt • 1 agent ctrl+x down • 1 queued ctrl+x q")
     expect(app.captureCharFrame()).toContain("2 queued")
-    expect(app.captureCharFrame()).not.toContain("agent ·  ·")
+    expect(app.captureCharFrame()).not.toContain("to view")
+    expect(app.captureCharFrame()).not.toContain("edit/remove")
   } finally {
     app.renderer.currentFocusedRenderable?.blur()
     app.renderer.currentFocusedEditor?.blur()