From d39408afc4c33b3ddf9147a8d319093f37249316 Mon Sep 17 00:00:00 2001 From: StanPonomarev Date: Fri, 18 Sep 2026 10:08:13 +0200 Subject: [PATCH] move the quota to the prompt --- src/tui.tsx | 43 +++++++++++++++++++++++++++---------------- tests/tui.test.ts | 36 ++++++++++++++++++++++++++++++------ 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/src/tui.tsx b/src/tui.tsx index d6e1b90..52e3d52 100644 --- a/src/tui.tsx +++ b/src/tui.tsx @@ -60,32 +60,43 @@ export default Plugin.define({ setRevision((value) => value + 1); }); - const Slot = (props: { instance: string }) => { + const Slot = (props: { instance: string; centered?: boolean; visible?: () => boolean }) => { const [status] = createResource( - () => `${props.instance}:${revision()}`, + () => (props.visible?.() === false ? false : `${props.instance}:${revision()}`), () => loadQuotaStatus(() => quota.status({})), ); + const Content = () => ( + + + ChatGPT quota ({status()!.plan}) + + + {(window) => ( + {formatQuotaWindow(window)} + )} + + + ); return ( - - - ChatGPT quota ({status()!.plan}) - - - {(window) => ( - - {formatQuotaWindow(window)} - - )} - - + }> + + + + ); }; const removeHome = context.ui.slot({ - append: "home.footer.status", - render: () => , + after: "prompt.footer", + render: () => ( + context.ui.router.current().type === "home"} + /> + ), }); const removeSidebar = context.ui.slot({ append: "sidebar.footer", diff --git a/tests/tui.test.ts b/tests/tui.test.ts index 323b62b..e9afa15 100644 --- a/tests/tui.test.ts +++ b/tests/tui.test.ts @@ -1,12 +1,13 @@ import { expect, test } from "bun:test"; import type { Plugin } from "@opencode/plugin/tui"; +import type { Route, SlotMap } from "@opencode/plugin/tui/context"; import { testRender, type JSX } from "@opentui/solid"; import { createSignal } from "solid-js"; import { build } from "../scripts/build.js"; import type { QuotaStatus } from "../src/quota.js"; type SidebarSlotRender = (input: { readonly sessionID: string }) => JSX.Element; -type HomeSlotRender = (input: Record) => JSX.Element; +type HomeSlotRender = (input: SlotMap["prompt.footer"]) => JSX.Element; const status = (plan: string, remaining: number): QuotaStatus => ({ plan, @@ -39,6 +40,7 @@ test("built quota slot stays reactive across pending, empty, failure, update, an let sidebarRender: SidebarSlotRender | undefined; let homeRender: HomeSlotRender | undefined; let notifyUpdated: (() => void) | undefined; + const [route, setRoute] = createSignal({ type: "home" }); const quota = { status() { @@ -58,11 +60,12 @@ test("built quota slot stays reactive across pending, empty, failure, update, an client: { rpc: () => quota }, ui: { slot(claim: { - append: "home.footer.status" | "sidebar.footer"; + after?: "prompt.footer"; + append?: "sidebar.footer"; render: HomeSlotRender | SidebarSlotRender; }) { - if (claim.append === "home.footer.status") { - // SAFETY: The home slot renderer receives the empty home slot input. + if (claim.after === "prompt.footer") { + // SAFETY: The prompt footer renderer receives the matching slot input. homeRender = claim.render as HomeSlotRender; } else { // SAFETY: The only other fixture path is the session-aware sidebar slot. @@ -70,6 +73,7 @@ test("built quota slot stays reactive across pending, empty, failure, update, an } return () => undefined; }, + router: { current: route }, }, theme: { text: { default: "#ffffff", muted: "#888888" } }, }; @@ -81,12 +85,32 @@ test("built quota slot stays reactive across pending, empty, failure, update, an throw new Error("Plugin did not register its slots"); const renderHome = homeRender; - const home = await testRender(() => renderHome({}), { width: 70, height: 5 }); + const home = await testRender(() => renderHome({ mode: "normal", showDetails: false }), { + width: 40, + height: 5, + }); await home.waitForFrame((frame) => frame.includes("ChatGPT quota (home)")); - expect(home.captureCharFrame()).toContain("5h: 90% · due"); + const homeFrame = home.captureCharFrame(); + expect(homeFrame).toContain(" ChatGPT quota (home)"); + expect(homeFrame).toContain(" 5h: 90% · due"); expect(statusCalls).toBe(1); home.renderer.destroy(); + setRoute({ type: "session", sessionID: "session-one" }); + const sessionPrompt = await testRender( + () => + renderHome({ + sessionID: "session-one", + mode: "normal", + showDetails: false, + }), + { width: 40, height: 5 }, + ); + await sessionPrompt.renderOnce(); + expect(sessionPrompt.captureCharFrame()).not.toContain("ChatGPT quota"); + expect(statusCalls).toBe(1); + sessionPrompt.renderer.destroy(); + const [sessionID, setSessionID] = createSignal("session-one"); const renderSlot = sidebarRender; const setup = await testRender(