Compare commits

...
6 changed files with 96 additions and 0 deletions

No files matched your search

@@ -1,5 +1,25 @@
import { expect, story } from "../../storybook/playwright/story"
story("spaces an error between a shell result and expanded updates", async ({ mount }) => {
const timeline = await mount("current-session-timeline-rows--error-and-updates")
const shell = timeline.locator('[data-component="bash-output"]')
const error = timeline.locator('[data-kind="session-error-card"]')
const updates = timeline.locator('[data-component="collapsed-tool-group"] [data-slot="collapsible-trigger"]')
const shellBox = (await shell.boundingBox())!
const errorBox = (await error.boundingBox())!
const updatesBox = (await updates.boundingBox())!
expect(errorBox.y - (shellBox.y + shellBox.height)).toBe(24)
expect(updatesBox.y - (errorBox.y + errorBox.height)).toBe(8)
expect(updatesBox.x).toBe(errorBox.x)
await updates.click()
const notice = timeline.locator('[data-slot="session-timeline-notice"]')
await expect(notice).toContainText("Instructions updated")
const expandedBox = (await updates.boundingBox())!
expect((await notice.boundingBox())!.y - (expandedBox.y + expandedBox.height)).toBe(0)
expect((await notice.boundingBox())!.x - expandedBox.x).toBe(12)
expect(expandedBox.height).toBe(28)
})
for (const streaming of [false, true]) {
story(`renders Mermaid in the ${streaming ? "streaming" : "completed"} timeline`, async ({ mount, page }) => {
await page.setViewportSize({ width: 390, height: 900 })
@@ -676,6 +676,10 @@
}
}
[data-component="collapsed-tool-group"][data-notices-only] > [data-component="collapsible"].tool-collapsible {
--tool-content-gap: 0px;
}
[data-component="context-tool-group-list"] {
padding: 0;
display: flex;
@@ -277,6 +277,21 @@
}
}
[data-component="session-timeline"]
> [data-timeline-row="Error"]
+ :is([data-timeline-row="AssistantPart"], [data-timeline-row="Notice"]) {
padding-top: 16px;
}
[data-component="session-timeline"]
> [data-timeline-row="Error"]
+ :is(
[data-timeline-row="AssistantPart"]:has([data-notices-only]),
[data-timeline-row="Notice"]:has([data-slot="session-timeline-notice"])
) {
padding-top: 8px;
}
[data-slot="session-turn-list"] {
gap: 24px;
}
@@ -390,6 +390,7 @@ export function createSessionTimelineRowRenderer(input: {
"md:max-w-[1000px] md:mx-auto": input.centered?.(),
"pt-2": props.row._tag === "AssistantPart" && props.row.spacing === "tool",
"pt-4": props.row._tag === "AssistantPart" && props.row.spacing === "content",
"pt-6": props.row._tag === "Error",
}}
>
<div data-component="session-turn" class="min-w-0 w-full relative" style={{ height: "auto" }}>
@@ -3,6 +3,7 @@ import { createMemo } from "solid-js"
import { createStore } from "solid-js/store"
import type { SessionDocument } from "../document"
import { SessionTimeline } from "./session-timeline"
import { timelinePresets } from "./detail"
import type { ReasoningMode } from "./projection"
import { CurrentSessionProviders, CurrentSessionTimelineStory } from "../storybook/current-session-story"
import {
@@ -622,3 +623,57 @@ export const InstructionsUpdatedMultiple = {
/>
),
}
export const ErrorAndUpdates = {
render: () => {
const document = {
...thinkingDocument,
status: { type: "idle" },
messages: [
{
id: "msg_user_shell_error",
type: "user",
text: "Check the shell command and its result.",
metadata: { agent: "build", model: STORY_MODEL },
time: { created: STORY_TIME },
},
{
id: "msg_shell_error",
type: "assistant",
agent: "build",
model: STORY_MODEL,
content: [
{
type: "tool",
id: "tool_shell_error",
name: "shell",
state: {
status: "completed",
input: { command: "cd ~/Documents/Local/opencode && ls packages/tui" },
content: [{ type: "text", text: "AGENTS.md\nbunfig.toml\nnode_modules\npackage.json\nsrc" }],
metadata: {},
},
time: { created: STORY_TIME + 100, ran: STORY_TIME + 150, completed: STORY_TIME + 300 },
},
],
error: { type: "ProviderError", message: "getaddrinfo ENOTFOUND opencode.ai" },
time: { created: STORY_TIME + 50, completed: STORY_TIME + 400 },
},
{
id: "msg_after_error_updates",
type: "system",
description: "Instructions updated: core/codemode, core/mcp-guidance",
text: "Instructions updated: core/codemode, core/mcp-guidance",
time: { created: STORY_TIME + 500 },
},
],
} satisfies SessionDocument
return (
<section class="mx-auto min-h-screen w-full max-w-[720px] bg-v2-background-bg-base p-6">
<CurrentSessionProviders document={document}>
<SessionTimeline document={document} timelineDetail={timelinePresets[1].value} />
</CurrentSessionProviders>
</section>
)
},
}
@@ -623,6 +623,7 @@ export function CurrentContextToolGroup(props: {
<div
ref={root}
data-component="collapsed-tool-group"
data-notices-only={props.parts.every((part) => part.type === "notice") ? "" : undefined}
data-timeline-part-ids={props.parts.map((part) => part.id).join(",")}
>
<BasicTool