chore(tui): remove turn flash scaffolding

This commit is contained in:
Kit Langton
2026-08-14 14:14:03 -04:00
parent c49f71185d
commit 0cb7fcc49a
4 changed files with 2 additions and 216 deletions
+1 -1
View File
@@ -4,6 +4,6 @@
- Put the current treatment and meaningfully different variants in the story. Expose replay and tuning controls in `StoryFooter`, including reset when values are adjustable.
- Let the user choose or tune a variant in the story before selecting production defaults.
- After selection, register the behavior in `src/component/dialog-experiments.tsx` and gate it with `config.data.experimental?.<id> === true`; experiments must not change default behavior.
- Keep the story as the safe tuning and regression fixture for the experiment.
- Treat tuning-only stories as local scaffolding and remove them and their registration before committing. Commit a story only when the user explicitly wants it retained as a reusable regression fixture.
- Use OpenCode Drive with a simulated LLM for deterministic turn/session behavior. Do not invoke a real model only to verify TUI behavior.
- Run the story with `OPENCODE_STORY=<story-id> bun run dev:live` and exercise relevant wide and narrow terminal sizes.
@@ -4,7 +4,6 @@ import { createSignal, For, type JSX } from "solid-js"
import { StoryFooter } from "./footer"
import { sessionTabsStory } from "./session-tabs"
import { sessionLocationMissingStory } from "./session-location-missing"
import { turnSummaryStory } from "./turn-summary"
/**
* A story is a full-screen, fixture-driven simulation of a real production component. Stories own
@@ -16,7 +15,7 @@ export type Story = {
render: (context: Plugin.Context) => JSX.Element
}
const stories: Story[] = [sessionTabsStory, turnSummaryStory, sessionLocationMissingStory]
const stories: Story[] = [sessionTabsStory, sessionLocationMissingStory]
function Commands(props: { context: Plugin.Context }) {
props.context.keymap.layer(() => ({
@@ -1,145 +0,0 @@
import { Plugin } from "@opencode-ai/plugin/tui"
import { useTerminalDimensions } from "@opentui/solid"
import { createSignal, For } from "solid-js"
import { createStore } from "solid-js/store"
import { AssistantSummary } from "../../../component/assistant-summary"
import { useLocal } from "../../../context/local"
import { StoryFooter } from "./footer"
import type { Story } from "./index"
const PRESETS = [
{ name: "Snap", duration: 0.18, intensity: 0.9 },
{ name: "Current", duration: 0.32, intensity: 0.75 },
{ name: "Gentle", duration: 0.5, intensity: 0.6 },
{ name: "Lingering", duration: 0.8, intensity: 0.7 },
]
function TurnSummaryStory(props: { context: Plugin.Context }) {
const dimensions = useTerminalDimensions()
const theme = props.context.theme
const local = useLocal()
const [presets, setPresets] = createStore(PRESETS.map((preset) => ({ ...preset, trigger: 0 })))
const [selected, setSelected] = createSignal(1)
const [lastEvent, setLastEvent] = createSignal("press space to compare all four")
const replay = (index?: number) => {
if (index === undefined) {
presets.forEach((_, presetIndex) => setPresets(presetIndex, "trigger", (trigger) => trigger + 1))
setLastEvent("replayed all variants")
return
}
setPresets(index, "trigger", (trigger) => trigger + 1)
setLastEvent(`replayed ${presets[index]!.name.toLowerCase()}`)
}
const adjustDuration = (delta: number) => {
const index = selected()
setPresets(index, "duration", (duration) => Math.max(0.05, Math.round((duration + delta) * 100) / 100))
replay(index)
}
const adjustIntensity = (delta: number) => {
const index = selected()
setPresets(index, "intensity", (intensity) => Math.max(0.1, Math.min(1, intensity + delta)))
replay(index)
}
const reset = () => {
PRESETS.forEach((preset, index) => setPresets(index, { ...preset, trigger: presets[index]!.trigger + 1 }))
setSelected(1)
setLastEvent("reset presets and replayed all variants")
}
props.context.keymap.layer(() => ({
commands: [
{
bind: "escape",
title: "Back to storybook",
group: "Storybook",
run: () => props.context.ui.router.navigate({ type: "plugin", name: "storybook" }),
},
{ bind: "space", title: "Replay all variants", group: "Storybook", run: () => replay() },
...PRESETS.map((preset, index) => ({
bind: String(index + 1),
title: `Replay ${preset.name}`,
group: "Storybook",
run: () => {
setSelected(index)
replay(index)
},
})),
{
bind: "up,k",
title: "Select previous variant",
group: "Storybook",
run: () => setSelected((index) => (index + presets.length - 1) % presets.length),
},
{
bind: "down,j",
title: "Select next variant",
group: "Storybook",
run: () => setSelected((index) => (index + 1) % presets.length),
},
{ bind: "left,h", title: "Shorten fade", group: "Storybook", run: () => adjustDuration(-0.05) },
{ bind: "right,l", title: "Lengthen fade", group: "Storybook", run: () => adjustDuration(0.05) },
{ bind: "-", title: "Dim flash", group: "Storybook", run: () => adjustIntensity(-0.05) },
{ bind: "+,=", title: "Brighten flash", group: "Storybook", run: () => adjustIntensity(0.05) },
{ bind: "r", title: "Reset variants", group: "Storybook", run: reset },
],
}))
return (
<box
width={dimensions().width}
height={dimensions().height}
flexDirection="column"
backgroundColor={theme.background.default}
>
<box paddingTop={2} paddingLeft={3} paddingRight={3} flexDirection="column" flexGrow={1}>
<text fg={theme.text.default}>turn summary flash</text>
<text fg={theme.text.subdued}>compare the production completion treatment across timing presets</text>
<box height={2} />
<For each={presets}>
{(preset, index) => (
<box flexDirection="column" marginBottom={1}>
<text fg={index() === selected() ? theme.text.default : theme.text.subdued}>
{index() === selected() ? "" : " "} {index() + 1} {preset.name.padEnd(10)} {preset.duration.toFixed(2)}s
{" "}{Math.round(preset.intensity * 100)}%
</text>
<box paddingLeft={4}>
<AssistantSummary
agent="Build"
model="GPT-5.6 Sol Fast"
duration="10.5s"
agentColor={local.agent.color("build")}
subduedColor={theme.text.subdued}
flashColor={theme.text.default}
animations
flash={preset}
/>
</box>
</box>
)}
</For>
</box>
<StoryFooter
context={props.context}
title="storybook / turn summary"
status={`${presets[selected()]!.name} ${presets[selected()]!.duration.toFixed(2)}s ${Math.round(presets[selected()]!.intensity * 100)}%`}
message={lastEvent()}
controls={[
{ shortcut: "space", label: "replay all" },
{ shortcut: "1-4", label: "replay one" },
{ shortcut: "↑/↓", label: "select" },
{ shortcut: "←/→", label: "timing" },
{ shortcut: "-/+", label: "brightness" },
{ shortcut: "r", label: "reset" },
{ shortcut: "esc", label: "back" },
]}
/>
</box>
)
}
export const turnSummaryStory: Story = {
id: "turn-summary",
title: "Turn summary flash",
render: (context) => <TurnSummaryStory context={context} />,
}
@@ -1,68 +0,0 @@
/** @jsxImportSource @opentui/solid */
import { RGBA } from "@opentui/core"
import { testRender } from "@opentui/solid"
import { expect, test } from "bun:test"
import { AssistantSummary } from "../../src/component/assistant-summary"
const agent = RGBA.fromHex("#6699ff")
const subdued = RGBA.fromHex("#667085")
const text = RGBA.fromHex("#f0f2ff")
test("flashes a completed summary and settles to its semantic colors", async () => {
const app = await testRender(
() => (
<AssistantSummary
agent="Build"
model="Simulated Model"
duration="800ms"
agentColor={agent}
subduedColor={subdued}
flashColor={text}
animations
flash={{ trigger: 1, duration: 0.8, intensity: 0.7 }}
/>
),
{ width: 60, height: 1 },
)
try {
await app.renderOnce()
const flashed = app.renderer.currentRenderBuffer.getSpanLines()[0]!.spans.filter((span) => span.text.trim())
expect(flashed[0]!.fg.equals(agent)).toBeFalse()
expect(flashed[1]!.fg.equals(subdued)).toBeFalse()
await Bun.sleep(900)
await app.renderOnce()
const settled = app.renderer.currentRenderBuffer.getSpanLines()[0]!.spans.filter((span) => span.text.trim())
expect(settled[0]!.fg.equals(agent)).toBeTrue()
expect(settled[1]!.fg.equals(subdued)).toBeTrue()
} finally {
app.renderer.destroy()
}
})
test("stays at rest when animations are disabled", async () => {
const app = await testRender(
() => (
<AssistantSummary
agent="Build"
model="Simulated Model"
agentColor={agent}
subduedColor={subdued}
flashColor={text}
animations={false}
flash={{ trigger: 1, duration: 0.8, intensity: 0.7 }}
/>
),
{ width: 60, height: 1 },
)
try {
await app.renderOnce()
const spans = app.renderer.currentRenderBuffer.getSpanLines()[0]!.spans.filter((span) => span.text.trim())
expect(spans[0]!.fg.equals(agent)).toBeTrue()
expect(spans[1]!.fg.equals(subdued)).toBeTrue()
} finally {
app.renderer.destroy()
}
})