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(