mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-14 03:56:55 +03:00
feat(tui): show Go allowance when a session costs nothing (#58565)
This commit is contained in:
@@ -43,9 +43,12 @@ export function PromptFooter(props: {
|
||||
session.revert?.messageID,
|
||||
)
|
||||
const cost = props.context.data.session.cost(props.sessionID)
|
||||
// A subscription such as OpenCode Go covers its requests from an allowance, so they cost nothing.
|
||||
const allowance = props.context.data.session.allowance(props.sessionID)
|
||||
return [
|
||||
usage ? formatContextUsage(usage.tokens, usage.percent) : undefined,
|
||||
cost > 0 ? money.format(cost) : undefined,
|
||||
allowance > 0 ? `${money.format(allowance)} allowance` : undefined,
|
||||
].filter((item): item is string => Boolean(item))
|
||||
})
|
||||
const live = createMemo(() => Boolean(subagents() || shells()))
|
||||
|
||||
@@ -12,13 +12,14 @@ export function SidebarContext(props: { context: Plugin.Context; sessionID: stri
|
||||
const msg = createMemo(() => props.context.data.session.message.list(props.sessionID))
|
||||
const session = createMemo(() => props.context.data.session.get(props.sessionID))
|
||||
const cost = createMemo(() => props.context.data.session.cost(props.sessionID))
|
||||
const allowance = createMemo(() => props.context.data.session.allowance(props.sessionID))
|
||||
|
||||
const state = createMemo(() =>
|
||||
contextUsage(msg(), props.context.data.location.model.list(session()?.location), session()?.revert?.messageID),
|
||||
)
|
||||
|
||||
return (
|
||||
<Show when={state() || cost() > 0}>
|
||||
<Show when={state() || cost() > 0 || allowance() > 0}>
|
||||
<box>
|
||||
<text fg={theme.text.base}>
|
||||
<b>Context</b>
|
||||
@@ -36,6 +37,9 @@ export function SidebarContext(props: { context: Plugin.Context; sessionID: stri
|
||||
<Show when={cost() > 0}>
|
||||
<text fg={theme.text.muted}>{money.format(cost())} spent</text>
|
||||
</Show>
|
||||
<Show when={allowance() > 0}>
|
||||
<text fg={theme.text.muted}>{money.format(allowance())} allowance used</text>
|
||||
</Show>
|
||||
</box>
|
||||
</Show>
|
||||
)
|
||||
|
||||
@@ -29,6 +29,7 @@ test("prompt footer separates simultaneous subagent, shell, and usage status", a
|
||||
status: (id: string) => (id === "child" ? "running" : "idle"),
|
||||
get: () => ({ id: "session", location: { directory: "/workspace" } }),
|
||||
cost: () => 1,
|
||||
allowance: () => 0,
|
||||
message: { list: () => [] },
|
||||
},
|
||||
shell: {
|
||||
@@ -87,6 +88,7 @@ test("prompt footer can hide details", async () => {
|
||||
status: () => "running",
|
||||
get: () => ({ id: "session", location: { directory: "/workspace" } }),
|
||||
cost: () => 1,
|
||||
allowance: () => 0,
|
||||
message: {
|
||||
list: () => [
|
||||
{
|
||||
@@ -142,3 +144,36 @@ test("prompt footer can hide details", async () => {
|
||||
app.renderer.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
test("prompt footer shows the allowance a subscription covered", async () => {
|
||||
const color = RGBA.fromInts(200, 200, 200)
|
||||
const context = {
|
||||
location: { directory: "/workspace" },
|
||||
theme: { text: { base: color, muted: color } },
|
||||
keymap: { shortcuts: () => [] },
|
||||
data: {
|
||||
session: {
|
||||
family: () => ["session"],
|
||||
status: () => "idle",
|
||||
get: () => ({ id: "session", location: { directory: "/workspace" } }),
|
||||
cost: () => 0,
|
||||
allowance: () => 0.42,
|
||||
message: { list: () => [] },
|
||||
},
|
||||
shell: { list: () => [] },
|
||||
location: { model: { list: () => [] } },
|
||||
},
|
||||
} as unknown as Context
|
||||
const app = await testRender(
|
||||
() => <PromptFooter context={context} sessionID="session" mode="normal" showDetails={true} />,
|
||||
{ width: 80, height: 1 },
|
||||
)
|
||||
|
||||
try {
|
||||
await app.renderOnce()
|
||||
expect(app.captureCharFrame()).toContain("$0.42 allowance")
|
||||
expect(app.captureCharFrame()).not.toContain("$0.00")
|
||||
} finally {
|
||||
app.renderer.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -5,7 +5,7 @@ import { testRender } from "@opentui/solid"
|
||||
import type { Context } from "@opencode/plugin/tui/context"
|
||||
import { SidebarContext } from "../../src/feature-plugins/sidebar/context"
|
||||
|
||||
function context(options?: { cost?: number; tokens?: number }) {
|
||||
function context(options?: { cost?: number; allowance?: number; tokens?: number }) {
|
||||
const color = RGBA.fromInts(200, 200, 200)
|
||||
return {
|
||||
theme: { text: { base: color, muted: color } },
|
||||
@@ -13,6 +13,7 @@ function context(options?: { cost?: number; tokens?: number }) {
|
||||
session: {
|
||||
get: () => ({ location: { directory: "/workspace" } }),
|
||||
cost: () => options?.cost ?? 0,
|
||||
allowance: () => options?.allowance ?? 0,
|
||||
message: {
|
||||
list: () =>
|
||||
options?.tokens
|
||||
@@ -68,3 +69,19 @@ test("sidebar shows available context usage", async () => {
|
||||
app.renderer.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
test("sidebar shows the allowance a subscription covered", async () => {
|
||||
const app = await testRender(() => <SidebarContext context={context({ allowance: 0.42 })} sessionID="session" />, {
|
||||
width: 42,
|
||||
height: 8,
|
||||
})
|
||||
|
||||
try {
|
||||
await app.renderOnce()
|
||||
expect(app.captureCharFrame()).toContain("Context")
|
||||
expect(app.captureCharFrame()).toContain("$0.42 allowance used")
|
||||
expect(app.captureCharFrame()).not.toContain("spent")
|
||||
} finally {
|
||||
app.renderer.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user