Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions packages/session-ui/component-tests/session-timeline.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,30 @@ story("aligns the retry icon with the error label", async ({ mount }) => {
expect(iconY).toBe(labelY)
})

story("centers the error icon on the first line of short and wrapped errors", async ({ mount }) => {
const timeline = await mount("current-session-error-card--provider-errors")
const cards = timeline.locator('[data-kind="session-error-card"]')

await expect(cards).toHaveCount(2)

const geometry = await cards.evaluateAll((elements) =>
elements.map((card) => {
const icon = card.querySelector('[data-slot="icon-svg"]')!.getBoundingClientRect()
const message = card.querySelector('[data-slot="icon-svg"] + div')!
const line = parseFloat(getComputedStyle(message).lineHeight)
const box = message.getBoundingClientRect()

return {
lines: Math.round(box.height / line),
offset: icon.top + icon.height / 2 - (box.top + line / 2),
}
}),
)

expect(geometry.map((card) => card.lines > 1)).toEqual([false, true])
geometry.forEach((card) => expect(card.offset).toBeCloseTo(0, 0))
})

// Moved from packages/app/e2e/regression/session-timeline-context-state.spec.ts
story("preserves a collapsed context group through count and status updates", async ({ mount }) => {
const timeline = await mount("current-session-research-agents--agent-research", { args: { scenario: "exploration" } })
Expand Down
2 changes: 1 addition & 1 deletion packages/session-ui/src/components/session-error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function SessionErrorMessage(props: { message: string }) {
export function SessionError(props: { message: string }) {
return (
<Card variant="error" class="error-card" data-kind="session-error-card">
<div class="flex w-full min-w-0 items-center gap-2">
<div class="flex w-full min-w-0 items-start gap-2">
<Icon name="outline-hexagonal-warning" class="shrink-0 text-v2-state-fg-danger" />
<div class="min-w-0">
<SessionErrorMessage message={props.message} />
Expand Down
54 changes: 54 additions & 0 deletions packages/session-ui/src/timeline/error-card.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { SessionDocument } from "../document"
import { CURRENT_SESSION_ID, STORY_MODEL, STORY_TIME, thinkingDocument } from "../storybook/current-session-fixtures"
import { CurrentSessionTimelineStory } from "../storybook/current-session-story"
import { SessionTimeline } from "./session-timeline"

export default {
title: "OpenCode/Conversation/Error card",
id: "current-session-error-card",
component: SessionTimeline,
parameters: { layout: "fullscreen" },
}

const document = {
sessionID: CURRENT_SESSION_ID,
messages: [
...thinkingDocument.messages,
{
id: "msg_story_error_short",
type: "assistant",
agent: "build",
model: STORY_MODEL,
content: [],
error: { type: "provider.content-filter", message: "Provider blocked the response: Request declined." },
time: { created: STORY_TIME + 33_000, completed: STORY_TIME + 34_000 },
},
{ id: "msg_story_error_retry", type: "user", text: "Try again.", time: { created: STORY_TIME + 34_500 } },
{
id: "msg_story_error_long",
type: "assistant",
agent: "build",
model: STORY_MODEL,
content: [],
error: {
type: "provider.content-filter",
message:
"Provider blocked the response: This request could not be completed. Check the provider's documentation for the applicable policy, then try a different approach or model.",
},
time: { created: STORY_TIME + 35_000, completed: STORY_TIME + 36_000 },
},
],
status: { type: "idle" },
diffs: [],
} satisfies SessionDocument

export const ProviderErrors = {
render: () => (
<CurrentSessionTimelineStory
title="Provider errors"
description="Short and wrapped provider errors in the production timeline."
document={document}
width="480px"
/>
),
}
Loading