mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-13 19:46:16 +03:00
Session message style (#46538)
Co-authored-by: David Hill <iamdavidhill@gmail.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
71779ae5de
commit
d46ed9e9db
@@ -75,11 +75,12 @@ for (const theme of ["light", "dark"] as const) {
|
||||
await expectBackground(view.send, "contrast")
|
||||
const message = page.locator('[data-slot="user-message-text"]')
|
||||
await expect(message).toHaveText("Check this fixture workspace.")
|
||||
await expectBackground(
|
||||
await expectToken(
|
||||
message,
|
||||
scenario.accent ? "accent" : theme === "light" ? "layer-02" : "layer-01",
|
||||
"background-color",
|
||||
scenario.accent ? "--v2-background-bg-accent" : "--v2-state-bg-info",
|
||||
)
|
||||
await expectToken(message, "color", scenario.accent ? "--v2-text-text-contrast" : "--v2-text-text-accent")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -245,3 +246,16 @@ async function expectBackground(element: Locator, token: string, property = "bac
|
||||
}, token)
|
||||
await expect(element).toHaveCSS(property, new RegExp(color.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")))
|
||||
}
|
||||
|
||||
async function expectToken(element: Locator, property: string, token: string) {
|
||||
const color = await element.evaluate((element, token) => {
|
||||
const probe = document.createElement("span")
|
||||
probe.hidden = true
|
||||
probe.style.color = `var(${token})`
|
||||
element.append(probe)
|
||||
const color = getComputedStyle(probe).color
|
||||
probe.remove()
|
||||
return color
|
||||
}, token)
|
||||
await expect(element).toHaveCSS(property, color)
|
||||
}
|
||||
|
||||
@@ -155,6 +155,16 @@ describe("isSessionNotFoundError", () => {
|
||||
expect(isSessionNotFoundError(new Error(body.message, { cause: { body, status: 404 } }), body.sessionID)).toBe(true)
|
||||
})
|
||||
|
||||
test("matches a structured error stored directly as the cause", () => {
|
||||
const body = {
|
||||
_tag: "SessionNotFoundError",
|
||||
sessionID: "ses_missing",
|
||||
message: "Session not found",
|
||||
} satisfies SessionNotFoundError
|
||||
|
||||
expect(isSessionNotFoundError(new Error("Unknown error", { cause: body }), body.sessionID)).toBe(true)
|
||||
})
|
||||
|
||||
test("rejects errors for other sessions and other 404 responses", () => {
|
||||
const body = {
|
||||
_tag: "SessionNotFoundError",
|
||||
|
||||
@@ -36,10 +36,9 @@ export function formatServerError(error: unknown, translate?: Translator, fallba
|
||||
}
|
||||
|
||||
function unwrapNamedError(error: unknown): unknown {
|
||||
if (error instanceof Error && error.cause && typeof error.cause === "object" && "body" in error.cause) {
|
||||
return (error.cause as Record<string, unknown>).body
|
||||
}
|
||||
return error
|
||||
if (!(error instanceof Error) || !error.cause || typeof error.cause !== "object") return error
|
||||
if ("body" in error.cause) return (error.cause as Record<string, unknown>).body
|
||||
return error.cause
|
||||
}
|
||||
|
||||
// Client-synthesized session not-found errors share one constructor and
|
||||
|
||||
@@ -478,7 +478,11 @@ export function createTimelineVirtualizer(input: Input) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="relative w-full h-full min-w-0" data-workspace-session={props.workspaceSession() ? "" : undefined}>
|
||||
<div
|
||||
class="relative w-full h-full min-w-0"
|
||||
data-workspace-session={props.workspaceSession() ? "" : undefined}
|
||||
data-local-session={!props.workspaceSession() ? "" : undefined}
|
||||
>
|
||||
<div
|
||||
class="absolute left-1/2 -translate-x-1/2 z-[60] pointer-events-none transition-all duration-200 ease-out"
|
||||
classList={{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { fileURLToPath } from "node:url"
|
||||
import type { Page } from "@playwright/test"
|
||||
import { expect, story } from "../../storybook/playwright/story"
|
||||
|
||||
const png = Buffer.from(
|
||||
@@ -129,6 +130,14 @@ story("mounts cached completed Markdown with sanitized HTML and decorations", as
|
||||
await expect(markdown.getByRole("heading")).toHaveText("Completed response")
|
||||
await expect(markdown.locator("script, [onerror], [href^='javascript:']")).toHaveCount(0)
|
||||
await expect(markdown.locator('code[data-inline-code-kind="path"]')).toHaveText("src/file.ts")
|
||||
await expect(markdown.locator('code[data-inline-code-kind="path"]')).toHaveCSS(
|
||||
"color",
|
||||
await resolvedColor(page, "--v2-text-text-code-path"),
|
||||
)
|
||||
await expect(markdown.locator('code[data-inline-code-kind="path"]')).toHaveCSS(
|
||||
"background-color",
|
||||
await resolvedColor(page, "--v2-background-bg-code-path"),
|
||||
)
|
||||
await expect(markdown.getByRole("link", { name: "https://example.com/docs" })).toHaveAttribute("target", "_blank")
|
||||
await expect(markdown.getByRole("link", { name: "https://example.com/docs" })).toHaveAttribute(
|
||||
"rel",
|
||||
@@ -153,6 +162,17 @@ story("mounts cached completed Markdown with sanitized HTML and decorations", as
|
||||
await expect(markdown).toHaveAttribute("data-markdown-ready", "")
|
||||
})
|
||||
|
||||
async function resolvedColor(page: Page, token: string) {
|
||||
return page.evaluate((token) => {
|
||||
const probe = document.createElement("span")
|
||||
probe.style.color = `var(${token})`
|
||||
document.body.append(probe)
|
||||
const color = getComputedStyle(probe).color
|
||||
probe.remove()
|
||||
return color
|
||||
}, token)
|
||||
}
|
||||
|
||||
story("keeps inline code backgrounds 18px tall", async ({ page }) => {
|
||||
await page.evaluate(async (fixture) => {
|
||||
const { mountMarkdown } = await import(fixture)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[data-component="markdown"] {
|
||||
--markdown-inline-code-path-color: var(--v2-text-text-code-accent);
|
||||
--markdown-inline-code-path-color: var(--v2-text-text-code-path);
|
||||
--markdown-inline-code-path-background: var(--v2-background-bg-code-path);
|
||||
--markdown-inline-code-color: var(--v2-text-text-code-accent);
|
||||
--markdown-inline-code-bg-mix: 8%;
|
||||
--markdown-shell-code-background: var(--v2-background-bg-layer-02);
|
||||
@@ -290,11 +291,7 @@
|
||||
|
||||
:not(pre) > code[data-inline-code-kind="path"] {
|
||||
color: var(--markdown-inline-code-path-color);
|
||||
background: color-mix(
|
||||
in oklch,
|
||||
var(--markdown-inline-code-path-color) var(--markdown-inline-code-bg-mix),
|
||||
transparent
|
||||
);
|
||||
background: var(--markdown-inline-code-path-background);
|
||||
}
|
||||
|
||||
a.external-link > code[data-inline-code-kind="url"] {
|
||||
|
||||
@@ -1547,3 +1547,9 @@
|
||||
background: var(--v2-background-bg-accent);
|
||||
color: var(--v2-text-text-contrast);
|
||||
}
|
||||
|
||||
:root body [data-local-session] [data-component="user-message"] [data-slot="user-message-text"] {
|
||||
background: var(--v2-state-bg-info);
|
||||
color: var(--v2-text-text-accent);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
--v2-background-bg-contrast: var(--v2-grey-1000);
|
||||
--v2-background-bg-button-neutral: var(--v2-grey-50);
|
||||
--v2-background-bg-accent: var(--v2-blue-600);
|
||||
--v2-background-bg-code-path: var(--v2-cyan-100);
|
||||
|
||||
/* ── Text ── */
|
||||
--v2-text-text-base: var(--v2-grey-1100);
|
||||
@@ -23,6 +24,7 @@
|
||||
--v2-text-text-accent: var(--v2-blue-600);
|
||||
--v2-text-text-accent-hover: var(--v2-blue-700);
|
||||
--v2-text-text-code-accent: var(--v2-blue-900);
|
||||
--v2-text-text-code-path: var(--v2-cyan-900);
|
||||
|
||||
/* ── Icon ── */
|
||||
--v2-icon-icon-base: var(--v2-grey-800);
|
||||
@@ -151,6 +153,7 @@
|
||||
--v2-background-bg-contrast: var(--v2-grey-700);
|
||||
--v2-background-bg-button-neutral: var(--v2-alpha-light-6);
|
||||
--v2-background-bg-accent: var(--v2-blue-600);
|
||||
--v2-background-bg-code-path: var(--v2-cyan-1200);
|
||||
|
||||
--v2-text-text-base: var(--v2-grey-200);
|
||||
--v2-text-text-muted: var(--v2-grey-500);
|
||||
@@ -160,6 +163,7 @@
|
||||
--v2-text-text-accent: var(--v2-blue-400);
|
||||
--v2-text-text-accent-hover: var(--v2-blue-300);
|
||||
--v2-text-text-code-accent: var(--v2-blue-400);
|
||||
--v2-text-text-code-path: var(--v2-cyan-400);
|
||||
|
||||
--v2-icon-icon-base: var(--v2-grey-400);
|
||||
--v2-icon-icon-muted: var(--v2-grey-600);
|
||||
@@ -265,6 +269,7 @@
|
||||
--v2-background-bg-contrast: var(--v2-grey-1000);
|
||||
--v2-background-bg-button-neutral: var(--v2-grey-50);
|
||||
--v2-background-bg-accent: var(--v2-blue-600);
|
||||
--v2-background-bg-code-path: var(--v2-cyan-100);
|
||||
|
||||
--v2-text-text-base: var(--v2-grey-1100);
|
||||
--v2-text-text-muted: var(--v2-grey-700);
|
||||
@@ -274,6 +279,7 @@
|
||||
--v2-text-text-accent: var(--v2-blue-600);
|
||||
--v2-text-text-accent-hover: var(--v2-blue-700);
|
||||
--v2-text-text-code-accent: var(--v2-blue-900);
|
||||
--v2-text-text-code-path: var(--v2-cyan-900);
|
||||
|
||||
--v2-icon-icon-base: var(--v2-grey-800);
|
||||
--v2-icon-icon-muted: var(--v2-grey-600);
|
||||
@@ -388,6 +394,7 @@
|
||||
--v2-background-bg-contrast: var(--v2-grey-700);
|
||||
--v2-background-bg-button-neutral: var(--v2-alpha-light-6);
|
||||
--v2-background-bg-accent: var(--v2-blue-600);
|
||||
--v2-background-bg-code-path: var(--v2-cyan-1200);
|
||||
|
||||
--v2-text-text-base: var(--v2-grey-100);
|
||||
--v2-text-text-muted: var(--v2-grey-500);
|
||||
@@ -397,6 +404,7 @@
|
||||
--v2-text-text-accent: var(--v2-blue-400);
|
||||
--v2-text-text-accent-hover: var(--v2-blue-300);
|
||||
--v2-text-text-code-accent: var(--v2-blue-400);
|
||||
--v2-text-text-code-path: var(--v2-cyan-400);
|
||||
|
||||
--v2-icon-icon-base: var(--v2-grey-400);
|
||||
--v2-icon-icon-muted: var(--v2-grey-600);
|
||||
|
||||
@@ -42,11 +42,13 @@ const light: Record<string, V2ColorValue> = {
|
||||
"v2-background-bg-contrast": ref("v2-grey-900"),
|
||||
"v2-background-bg-button-neutral": ref("v2-grey-100"),
|
||||
"v2-background-bg-accent": ref("v2-blue-600"),
|
||||
"v2-background-bg-code-path": ref("v2-cyan-100"),
|
||||
"v2-text-text-inverse": ref("v2-grey-100"),
|
||||
"v2-text-text-contrast": ref("v2-grey-100"),
|
||||
"v2-text-text-accent": ref("v2-blue-600"),
|
||||
"v2-text-text-accent-hover": ref("v2-blue-700"),
|
||||
"v2-text-text-code-accent": ref("v2-blue-900"),
|
||||
"v2-text-text-code-path": ref("v2-cyan-900"),
|
||||
"v2-border-border-muted": ref("v2-alpha-dark-8"),
|
||||
"v2-border-border-base": ref("v2-alpha-dark-10"),
|
||||
"v2-border-border-strong": ref("v2-alpha-dark-20"),
|
||||
@@ -107,11 +109,13 @@ const dark: Record<string, V2ColorValue> = {
|
||||
"v2-background-bg-contrast": ref("v2-grey-700"),
|
||||
"v2-background-bg-button-neutral": ref("v2-alpha-light-6"),
|
||||
"v2-background-bg-accent": ref("v2-blue-600"),
|
||||
"v2-background-bg-code-path": ref("v2-cyan-1200"),
|
||||
"v2-text-text-inverse": ref("v2-grey-1000"),
|
||||
"v2-text-text-contrast": ref("v2-grey-100"),
|
||||
"v2-text-text-accent": ref("v2-blue-400"),
|
||||
"v2-text-text-accent-hover": ref("v2-blue-300"),
|
||||
"v2-text-text-code-accent": ref("v2-blue-400"),
|
||||
"v2-text-text-code-path": ref("v2-cyan-400"),
|
||||
"v2-border-border-muted": ref("v2-alpha-light-8"),
|
||||
"v2-border-border-base": ref("v2-alpha-light-10"),
|
||||
"v2-border-border-strong": ref("v2-alpha-light-20"),
|
||||
|
||||
Reference in New Issue
Block a user