fix(tui): prevent sidebar scrollbar flash (#40056)
This commit is contained in:
parent
e872bd3c8f
commit
d5f6c088f0
3 changed files with 94 additions and 18 deletions
|
|
@ -7,7 +7,7 @@ const money = new Intl.NumberFormat("en-US", {
|
||||||
currency: "USD",
|
currency: "USD",
|
||||||
})
|
})
|
||||||
|
|
||||||
function View(props: { context: Plugin.Context; sessionID: string }) {
|
export function SidebarContext(props: { context: Plugin.Context; sessionID: string }) {
|
||||||
const theme = props.context.theme
|
const theme = props.context.theme
|
||||||
const msg = createMemo(() => props.context.data.session.message.list(props.sessionID))
|
const msg = createMemo(() => props.context.data.session.message.list(props.sessionID))
|
||||||
const session = createMemo(() => props.context.data.session.get(props.sessionID))
|
const session = createMemo(() => props.context.data.session.get(props.sessionID))
|
||||||
|
|
@ -18,28 +18,32 @@ function View(props: { context: Plugin.Context; sessionID: string }) {
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<box>
|
<Show when={state() || cost() > 0}>
|
||||||
<text fg={theme.text.default}>
|
<box>
|
||||||
<b>Context</b>
|
<text fg={theme.text.default}>
|
||||||
</text>
|
<b>Context</b>
|
||||||
<Show when={state()} fallback={<text fg={theme.text.subdued}>Not measured</text>}>
|
</text>
|
||||||
{(value) => (
|
<Show when={state()}>
|
||||||
<>
|
{(value) => (
|
||||||
<text fg={theme.text.subdued}>{value().tokens.toLocaleString()} tokens</text>
|
<>
|
||||||
<Show when={value().percent !== undefined}>
|
<text fg={theme.text.subdued}>{value().tokens.toLocaleString()} tokens</text>
|
||||||
<text fg={theme.text.subdued}>{value().percent}% used</text>
|
<Show when={value().percent !== undefined}>
|
||||||
</Show>
|
<text fg={theme.text.subdued}>{value().percent}% used</text>
|
||||||
</>
|
</Show>
|
||||||
)}
|
</>
|
||||||
</Show>
|
)}
|
||||||
<text fg={theme.text.subdued}>{money.format(cost())} spent</text>
|
</Show>
|
||||||
</box>
|
<Show when={cost() > 0}>
|
||||||
|
<text fg={theme.text.subdued}>{money.format(cost())} spent</text>
|
||||||
|
</Show>
|
||||||
|
</box>
|
||||||
|
</Show>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Plugin.define({
|
export default Plugin.define({
|
||||||
id: "internal:sidebar-context",
|
id: "internal:sidebar-context",
|
||||||
setup(context) {
|
setup(context) {
|
||||||
context.ui.slot("sidebar.content", (props) => <View context={context} sessionID={props.sessionID} />)
|
context.ui.slot("sidebar.content", (props) => <SidebarContext context={context} sessionID={props.sessionID} />)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,11 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) {
|
||||||
position={props.overlay ? "absolute" : "relative"}
|
position={props.overlay ? "absolute" : "relative"}
|
||||||
>
|
>
|
||||||
<scrollbox
|
<scrollbox
|
||||||
|
ref={(scroll) => queueMicrotask(() => scroll.verticalScrollBar.resetVisibilityControl())}
|
||||||
flexGrow={1}
|
flexGrow={1}
|
||||||
scrollAcceleration={scrollAcceleration()}
|
scrollAcceleration={scrollAcceleration()}
|
||||||
verticalScrollbarOptions={{
|
verticalScrollbarOptions={{
|
||||||
|
visible: false,
|
||||||
trackOptions: {
|
trackOptions: {
|
||||||
backgroundColor: theme.background.default,
|
backgroundColor: theme.background.default,
|
||||||
foregroundColor: theme.scrollbar.default,
|
foregroundColor: theme.scrollbar.default,
|
||||||
|
|
|
||||||
70
packages/tui/test/feature-plugins/sidebar-context.test.tsx
Normal file
70
packages/tui/test/feature-plugins/sidebar-context.test.tsx
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
/** @jsxImportSource @opentui/solid */
|
||||||
|
import { expect, test } from "bun:test"
|
||||||
|
import { RGBA } from "@opentui/core"
|
||||||
|
import { testRender } from "@opentui/solid"
|
||||||
|
import type { Context } from "@opencode-ai/plugin/tui/context"
|
||||||
|
import { SidebarContext } from "../../src/feature-plugins/sidebar/context"
|
||||||
|
|
||||||
|
function context(options?: { cost?: number; tokens?: number }) {
|
||||||
|
const color = RGBA.fromInts(200, 200, 200)
|
||||||
|
return {
|
||||||
|
theme: { text: { default: color, subdued: color } },
|
||||||
|
data: {
|
||||||
|
session: {
|
||||||
|
get: () => ({ location: { directory: "/workspace" } }),
|
||||||
|
cost: () => options?.cost ?? 0,
|
||||||
|
message: {
|
||||||
|
list: () =>
|
||||||
|
options?.tokens
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
id: "message",
|
||||||
|
type: "assistant",
|
||||||
|
model: { providerID: "provider", id: "model" },
|
||||||
|
tokens: {
|
||||||
|
input: options.tokens,
|
||||||
|
output: 0,
|
||||||
|
reasoning: 0,
|
||||||
|
cache: { read: 0, write: 0 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
location: {
|
||||||
|
model: { list: () => [] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as unknown as Context
|
||||||
|
}
|
||||||
|
|
||||||
|
test("sidebar omits context before usage is available", async () => {
|
||||||
|
const app = await testRender(() => <SidebarContext context={context()} sessionID="session" />, {
|
||||||
|
width: 42,
|
||||||
|
height: 8,
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
await app.renderOnce()
|
||||||
|
expect(app.captureCharFrame()).not.toContain("Context")
|
||||||
|
expect(app.captureCharFrame()).not.toContain("Not measured")
|
||||||
|
} finally {
|
||||||
|
app.renderer.destroy()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test("sidebar shows available context usage", async () => {
|
||||||
|
const app = await testRender(() => <SidebarContext context={context({ tokens: 1234 })} sessionID="session" />, {
|
||||||
|
width: 42,
|
||||||
|
height: 8,
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
await app.renderOnce()
|
||||||
|
expect(app.captureCharFrame()).toContain("Context")
|
||||||
|
expect(app.captureCharFrame()).toContain("1,234 tokens")
|
||||||
|
} finally {
|
||||||
|
app.renderer.destroy()
|
||||||
|
}
|
||||||
|
})
|
||||||
Loading…
Add table
Add a link
Reference in a new issue