Skip to content

Commit d68fd1e

Browse files
tylerslatonclaude
andcommitted
fix(showcase/harness): realign d5-chat-css probe with HALCYON theme
The chat-customization-css demo was re-skinned to the HALCYON parchment+ember theme; the probe still asserted the legacy hot-pink (#ff006e) user bubble and amber (#fde047) assistant bubble. New theme paints the user bubble's inner bg-muted element with the halcyon-paper-elevated background plus an ember (#c44a1f) left border in JetBrains Mono, and the assistant bubble in Fraunces serif on a transparent background. Replace the two background-color anchors with three independent signals — ember rgb on the user inner border-left, JetBrains Mono fragment on the user inner font-family (with fallback chain so Linux runners without the webfont still match), and Fraunces fragment on the assistant font-family. Each signal trips a distinct error so operators can tell which CSS layer broke. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c7be723 commit d68fd1e

2 files changed

Lines changed: 120 additions & 57 deletions

File tree

showcase/harness/src/probes/scripts/d5-chat-css.test.ts

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -52,38 +52,56 @@ describe("d5-chat-css script", () => {
5252
});
5353

5454
describe("validateChatCss", () => {
55-
it("returns null when both bubbles carry the expected RGB anchors", () => {
55+
it("returns null when all halcyon signals are present", () => {
5656
expect(
5757
validateChatCss({
58-
userBg:
59-
"linear-gradient(135deg, rgb(255, 0, 110) 0%, rgb(194, 24, 91) 100%) rgba(0, 0, 0, 0)",
60-
assistantBg: "rgb(253, 224, 71)",
58+
userBorderLeft: "rgb(196, 74, 31)",
59+
userFontFamily:
60+
'"JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace',
61+
assistantFontFamily:
62+
'"Fraunces", "Source Serif Pro", ui-serif, Georgia, "Times New Roman", serif',
6163
}),
6264
).toBeNull();
6365
});
6466

65-
it("returns error when user bubble missing red anchor", () => {
67+
it("returns error when user bubble missing ember left border", () => {
6668
expect(
6769
validateChatCss({
68-
userBg: "rgb(0, 0, 255)",
69-
assistantBg: "rgb(253, 224, 71)",
70+
userBorderLeft: "rgb(0, 0, 0)",
71+
userFontFamily: "JetBrains Mono",
72+
assistantFontFamily: "Fraunces",
7073
}),
71-
).toMatch(/red\/pink anchor/);
74+
).toMatch(/halcyon-ember left border/);
7275
});
7376

74-
it("returns error when assistant bubble missing yellow anchor", () => {
77+
it("returns error when user bubble missing halcyon-mono font", () => {
7578
expect(
7679
validateChatCss({
77-
userBg: "rgb(255, 0, 110)",
78-
assistantBg: "rgb(255, 255, 255)",
80+
userBorderLeft: "rgb(196, 74, 31)",
81+
userFontFamily: "Arial, sans-serif",
82+
assistantFontFamily: "Fraunces",
7983
}),
80-
).toMatch(/yellow\/amber anchor/);
84+
).toMatch(/halcyon-mono font/);
85+
});
86+
87+
it("returns error when assistant bubble missing halcyon-serif font", () => {
88+
expect(
89+
validateChatCss({
90+
userBorderLeft: "rgb(196, 74, 31)",
91+
userFontFamily: "JetBrains Mono",
92+
assistantFontFamily: "Arial, sans-serif",
93+
}),
94+
).toMatch(/halcyon-serif font/);
8195
});
8296

8397
it("returns error when bubbles missing entirely", () => {
84-
expect(validateChatCss({ userBg: null, assistantBg: null })).toMatch(
85-
/user bubble.*not found/,
86-
);
98+
expect(
99+
validateChatCss({
100+
userBorderLeft: null,
101+
userFontFamily: null,
102+
assistantFontFamily: null,
103+
}),
104+
).toMatch(/user bubble inner.*not found/);
87105
});
88106
});
89107

@@ -94,14 +112,16 @@ describe("d5-chat-css script", () => {
94112
).rejects.toThrow(/assistant bubble selector/);
95113
});
96114

97-
it("assertion succeeds when computed colors match", async () => {
115+
it("assertion succeeds when computed styles match", async () => {
98116
const assertion = buildChatCssAssertion();
99117
await expect(
100118
assertion(
101119
makePage({
102-
userBg:
103-
"linear-gradient(135deg, rgb(255, 0, 110) 0%, rgb(194, 24, 91) 100%)",
104-
assistantBg: "rgb(253, 224, 71)",
120+
userBorderLeft: "rgb(196, 74, 31)",
121+
userFontFamily:
122+
'"JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace',
123+
assistantFontFamily:
124+
'"Fraunces", "Source Serif Pro", ui-serif, Georgia, "Times New Roman", serif',
105125
}),
106126
),
107127
).resolves.toBeUndefined();

showcase/harness/src/probes/scripts/d5-chat-css.ts

Lines changed: 81 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,25 @@
22
* D5 — chat-css script.
33
*
44
* Drives `/demos/chat-customization-css` through one user turn and
5-
* verifies the demo's CSS theme is actually applied in the browser by
6-
* reading computed styles on the user-message and assistant-message
5+
* verifies the demo's HALCYON theme is actually applied in the browser
6+
* by reading computed styles on the user-message and assistant-message
77
* bubbles. The demo (see
88
* `showcase/integrations/langgraph-python/src/app/demos/chat-customization-css/theme.css`)
9-
* paints user bubbles with a hot-pink gradient (`#ff006e` → `#c2185b`)
10-
* and assistant bubbles with amber (`#fde047`). A failure here means
11-
* either the CSS import broke, the `chat-css-demo-scope` wrapper class
12-
* was lost, or the upstream `.copilotKit*` class names drifted.
9+
* paints user bubbles with a parchment paper-elevated background and
10+
* an ember left-border accent (`#c44a1f` → rgb(196, 74, 31)) on the
11+
* inner `[class*="bg-muted"]` element, plus JetBrains Mono. Assistant
12+
* messages use Fraunces serif on a transparent background with an
13+
* ember `::before` rule. A failure here means either the CSS import
14+
* broke, the `chat-css-demo-scope` wrapper class was lost, or the
15+
* upstream `.copilotKit*` class names drifted.
1316
*
14-
* Assertions look for the substantive theme color signature:
15-
* - user bubble's background contains `255, 0, 110` (the pink/red rgb
16-
* anchor) — gradient resolves to either `linear-gradient(...)` or to
17-
* a flattened color depending on browser
18-
* - assistant bubble's background resolves to `rgb(253, 224, 71)` (amber)
17+
* Three independent signals cover the theme — any one missing trips
18+
* the assertion with a specific error so operators don't have to
19+
* guess which layer broke:
20+
* 1. user bubble's inner element border-left-color contains
21+
* `196, 74, 31` (the halcyon-ember rgb anchor)
22+
* 2. user bubble's inner element font-family contains "JetBrains Mono"
23+
* 3. assistant bubble's font-family contains "Fraunces"
1924
*
2025
* One turn matches the recorded fixture (`chat-css.json`).
2126
*/
@@ -36,26 +41,46 @@ export const USER_BUBBLE_SELECTOR = ".copilotKitMessage.copilotKitUserMessage";
3641
/** Assistant-bubble selector. */
3742
export const ASSISTANT_BUBBLE_SELECTOR =
3843
".copilotKitMessage.copilotKitAssistantMessage";
44+
/** The user bubble's inner v2 element — paints the halcyon-paper-elevated
45+
* background plus the ember left border. The outer message wrapper is
46+
* transparent in the new theme; signals all live on this inner node. */
47+
export const USER_BUBBLE_INNER_SELECTOR = `${USER_BUBBLE_SELECTOR} [class*="bg-muted"]`;
3948

40-
/** Hot-pink rgb anchor present in the user-bubble gradient (#ff006e). */
41-
const USER_RGB_FRAGMENT = "255, 0, 110";
42-
/** Amber rgb on the assistant bubble (#fde047). */
43-
const ASSISTANT_RGB_FRAGMENT = "253, 224, 71";
49+
/** halcyon-ember rgb anchor (`#c44a1f`) on the user-bubble inner border-left. */
50+
const EMBER_RGB_FRAGMENT = "196, 74, 31";
51+
/** Editorial mono token (`var(--halcyon-mono)`) — JetBrains Mono with
52+
* a CSS fallback chain. The fallback covers Linux runners that lack
53+
* the webfont, so we accept either the real face or one of the
54+
* declared fallbacks. */
55+
const USER_MONO_FONT_FRAGMENTS = [
56+
"JetBrains Mono",
57+
// Fallback chain from `--halcyon-mono`.
58+
"ui-monospace",
59+
"SF Mono",
60+
"Menlo",
61+
"Consolas",
62+
];
63+
/** Editorial serif token (`var(--halcyon-serif)`) — Fraunces with
64+
* fallbacks. Same rationale as the mono fragments. */
65+
const ASSISTANT_SERIF_FONT_FRAGMENTS = [
66+
"Fraunces",
67+
"Source Serif Pro",
68+
"ui-serif",
69+
];
4470

4571
const PROBE_TIMEOUT_MS = 5_000;
4672

4773
/**
48-
* Probe-result shape: per-bubble computed background string. `null` means
49-
* the selector didn't match, which the assertion treats as
50-
* "missing bubble" — distinct from "matched but wrong color".
74+
* Probe-result shape: per-bubble computed strings. `null` means the
75+
* selector didn't match, distinct from "matched but wrong style".
5176
*/
5277
export interface ChatCssProbeResult {
53-
userBg: string | null;
54-
assistantBg: string | null;
78+
userBorderLeft: string | null;
79+
userFontFamily: string | null;
80+
assistantFontFamily: string | null;
5581
}
5682

57-
/** Read computed `background` / `background-color` for both bubbles
58-
* inside the demo scope.
83+
/** Read computed styles on both bubbles inside the demo scope.
5984
*
6085
* Notes on the page.evaluate body shape:
6186
* - We do NOT declare local const-arrows or TS interfaces inside the
@@ -70,39 +95,57 @@ export async function probeChatCss(page: Page): Promise<ChatCssProbeResult> {
7095
const win = globalThis as unknown as {
7196
document: { querySelector(sel: string): unknown };
7297
getComputedStyle(el: unknown): {
73-
background?: string;
74-
backgroundColor?: string;
98+
borderLeftColor?: string;
99+
fontFamily?: string;
75100
};
76101
};
77-
const userEl = win.document.querySelector(
102+
const userOuter = win.document.querySelector(
78103
".copilotKitMessage.copilotKitUserMessage",
79104
);
105+
// The inner node is the v2 framework's bubble that consumes the
106+
// `bg-muted` Tailwind utility — the demo's CSS hooks the substring
107+
// class-match because the upstream Tailwind class composition
108+
// includes `bg-muted` on the bubble div.
109+
const userInner = (userOuter as { querySelector?(sel: string): unknown } | null)?.querySelector?.(
110+
'[class*="bg-muted"]',
111+
);
80112
const assistantEl = win.document.querySelector(
81113
".copilotKitMessage.copilotKitAssistantMessage",
82114
);
83-
const userBg = userEl
84-
? `${win.getComputedStyle(userEl).background ?? ""} ${win.getComputedStyle(userEl).backgroundColor ?? ""}`.trim()
85-
: null;
86-
const assistantBg = assistantEl
87-
? `${win.getComputedStyle(assistantEl).background ?? ""} ${win.getComputedStyle(assistantEl).backgroundColor ?? ""}`.trim()
115+
const userStyle = userInner ? win.getComputedStyle(userInner) : null;
116+
const assistantStyle = assistantEl
117+
? win.getComputedStyle(assistantEl)
88118
: null;
89-
return { userBg, assistantBg };
119+
return {
120+
userBorderLeft: userStyle?.borderLeftColor ?? null,
121+
userFontFamily: userStyle?.fontFamily ?? null,
122+
assistantFontFamily: assistantStyle?.fontFamily ?? null,
123+
};
90124
})) as ChatCssProbeResult;
91125
}
92126

93127
/** Validate the probe — returns null on pass, error string on fail. */
94128
export function validateChatCss(probe: ChatCssProbeResult): string | null {
95-
if (probe.userBg === null) {
96-
return `chat-css: user bubble (${USER_BUBBLE_SELECTOR}) not found in DOM`;
129+
if (probe.userBorderLeft === null || probe.userFontFamily === null) {
130+
return `chat-css: user bubble inner (${USER_BUBBLE_INNER_SELECTOR}) not found in DOM`;
97131
}
98-
if (probe.assistantBg === null) {
132+
if (probe.assistantFontFamily === null) {
99133
return `chat-css: assistant bubble (${ASSISTANT_BUBBLE_SELECTOR}) not found in DOM`;
100134
}
101-
if (!probe.userBg.includes(USER_RGB_FRAGMENT)) {
102-
return `chat-css: user bubble background missing red/pink anchor (${USER_RGB_FRAGMENT}) — got "${probe.userBg.slice(0, 200)}"`;
135+
if (!probe.userBorderLeft.includes(EMBER_RGB_FRAGMENT)) {
136+
return `chat-css: user bubble missing halcyon-ember left border (${EMBER_RGB_FRAGMENT}) — got "${probe.userBorderLeft.slice(0, 200)}"`;
137+
}
138+
if (
139+
!USER_MONO_FONT_FRAGMENTS.some((f) => probe.userFontFamily!.includes(f))
140+
) {
141+
return `chat-css: user bubble missing halcyon-mono font (one of ${USER_MONO_FONT_FRAGMENTS.join(", ")}) — got "${probe.userFontFamily.slice(0, 200)}"`;
103142
}
104-
if (!probe.assistantBg.includes(ASSISTANT_RGB_FRAGMENT)) {
105-
return `chat-css: assistant bubble background missing yellow/amber anchor (${ASSISTANT_RGB_FRAGMENT}) — got "${probe.assistantBg.slice(0, 200)}"`;
143+
if (
144+
!ASSISTANT_SERIF_FONT_FRAGMENTS.some((f) =>
145+
probe.assistantFontFamily!.includes(f),
146+
)
147+
) {
148+
return `chat-css: assistant bubble missing halcyon-serif font (one of ${ASSISTANT_SERIF_FONT_FRAGMENTS.join(", ")}) — got "${probe.assistantFontFamily.slice(0, 200)}"`;
106149
}
107150
return null;
108151
}

0 commit comments

Comments
 (0)