Skip to content

Commit 033a7c7

Browse files
committed
Fix client script warning from theme provider
1 parent 6add4cb commit 033a7c7

70 files changed

Lines changed: 663 additions & 339 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/api/src/rest/routers/website.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ describe("website route GET /", () => {
105105
listWebsiteAccessUsersMock.mockResolvedValue([]);
106106
});
107107

108-
it("uses website-access users, normalizes blank names, and omits public email", async () => {
108+
it("uses website-access users, normalizes blank names, and includes email for avatar fallbacks", async () => {
109109
const { db, findManyMock, website } = createWebsiteContext();
110110
findManyMock.mockResolvedValue([{ id: "ai-1", name: "Support AI" }]);
111111
listWebsiteAccessUsersMock.mockResolvedValue([
@@ -151,17 +151,18 @@ describe("website route GET /", () => {
151151
{
152152
id: "user-1",
153153
name: null,
154+
email: "hidden@example.com",
154155
image: null,
155156
lastSeenAt: null,
156157
},
157158
{
158159
id: "user-2",
159160
name: "Alice",
161+
email: "alice@example.com",
160162
image: null,
161163
lastSeenAt: "2026-03-03T04:05:06.000Z",
162164
},
163165
]);
164-
expect(payload.availableHumanAgents[0]).not.toHaveProperty("email");
165166
expect(payload.lastOnlineAt).toBe("2026-03-03T04:05:06.000Z");
166167
});
167168

apps/api/src/rest/routers/website.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ websiteRouter.openapi(
102102
.map((humanAgent) => ({
103103
id: humanAgent.userId,
104104
name: normalizeHumanAgentName(humanAgent.name),
105+
email: humanAgent.email ?? null,
105106
image: humanAgent.image,
106107
lastSeenAt: humanAgent.lastSeenAt?.toISOString() ?? null,
107108
}));

apps/web/content/docs/advanced/primitives.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function CustomWidget() {
5555
<Primitives.Window>
5656
{({ close }) =>
5757
isOpen ? (
58-
<div className="fixed bottom-20 right-4 w-96 rounded-lg bg-white shadow-xl">
58+
<div className="fixed bottom-20 right-4 w-96 border bg-white shadow-xl">
5959
<button onClick={close} type="button">
6060
Close
6161
</button>

apps/web/content/docs/support-component/customization.mdx

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import { Support } from "@cossistant/react";
3636
}}
3737
slotProps={{
3838
content: {
39-
className: "rounded-[28px]",
39+
className: "border border-neutral-200 shadow-2xl",
4040
},
4141
}}
4242
/>;
@@ -68,24 +68,38 @@ Use `slots.trigger` when the launcher is the only thing that should change.
6868
import { Support, type SupportTriggerSlotProps } from "@cossistant/react";
6969
import * as React from "react";
7070

71-
const CustomBubble = React.forwardRef<HTMLButtonElement, SupportTriggerSlotProps>(
72-
function CustomBubble({ className, isOpen, toggle, unreadCount, ...props }, ref) {
71+
function mergeClassNames(...classes: Array<string | undefined>) {
72+
return classes.filter(Boolean).join(" ");
73+
}
74+
75+
const ClassicBubble = React.forwardRef<HTMLButtonElement, SupportTriggerSlotProps>(
76+
function ClassicBubbleTrigger(
77+
{ className, isOpen, isTyping: _isTyping, toggle, unreadCount, ...props },
78+
ref
79+
) {
7380
return (
7481
<button
7582
{...props}
76-
className={className}
83+
className={mergeClassNames(
84+
"relative flex size-14 items-center justify-center border border-black bg-black font-medium text-white text-sm shadow-xl transition-transform hover:scale-[1.02]",
85+
className
86+
)}
7787
onClick={toggle}
7888
ref={ref}
7989
type="button"
8090
>
8191
{isOpen ? "Close" : "Chat"}
82-
{unreadCount > 0 ? ` (${unreadCount})` : null}
92+
{unreadCount > 0 ? (
93+
<span className="-right-1 -top-1 absolute flex size-5 items-center justify-center border border-white bg-orange-500 text-[11px]">
94+
{unreadCount}
95+
</span>
96+
) : null}
8397
</button>
8498
);
8599
}
86100
);
87101

88-
<Support slots={{ trigger: CustomBubble }} />;
102+
<Support slots={{ trigger: ClassicBubble }} />;
89103
```
90104

91105
<ComponentPreview name="support-classic-bubble" />
@@ -98,17 +112,35 @@ The same slot can feel more like an in-product button instead of a floating chat
98112
import { Support, type SupportTriggerSlotProps } from "@cossistant/react";
99113
import * as React from "react";
100114

115+
function mergeClassNames(...classes: Array<string | undefined>) {
116+
return classes.filter(Boolean).join(" ");
117+
}
118+
101119
const PillBubble = React.forwardRef<HTMLButtonElement, SupportTriggerSlotProps>(
102-
function PillBubble({ className, isTyping, toggle, ...props }, ref) {
120+
function PillBubble(
121+
{
122+
className,
123+
isOpen: _isOpen,
124+
isTyping,
125+
unreadCount: _unreadCount,
126+
toggle,
127+
...props
128+
},
129+
ref
130+
) {
103131
return (
104132
<button
105133
{...props}
106-
className={className}
134+
className={mergeClassNames(
135+
"flex h-12 items-center gap-2 border border-black bg-white px-4 font-medium text-black text-sm shadow-xl transition-transform hover:scale-[1.01]",
136+
className
137+
)}
107138
onClick={toggle}
108139
ref={ref}
109140
type="button"
110141
>
111-
{isTyping ? "Support is typing..." : "Open support"}
142+
<span className="size-2 bg-orange-500" />
143+
<span>{isTyping ? "Support is typing..." : "Open support"}</span>
112144
</button>
113145
);
114146
}
@@ -154,7 +186,7 @@ function CustomHomePage({
154186
<Support
155187
slotProps={{
156188
content: {
157-
className: "rounded-3xl border shadow-2xl",
189+
className: "border shadow-2xl",
158190
},
159191
}}
160192
slots={{ homePage: CustomHomePage }}

apps/web/content/docs/support-component/hooks.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ export function CustomSupportButton() {
5252
return (
5353
<button
5454
onClick={toggle}
55-
className="relative rounded-lg bg-primary px-4 py-2 text-white"
55+
className="relative border border-primary bg-primary px-4 py-2 text-white"
5656
>
5757
Support
5858
{unreadCount > 0 && (
59-
<span className="absolute -right-1 -top-1 flex h-5 w-5 items-center justify-center rounded-full bg-red-500 text-xs">
59+
<span className="absolute -right-1 -top-1 flex h-5 w-5 items-center justify-center bg-red-500 text-xs">
6060
{unreadCount}
6161
</span>
6262
)}

apps/web/content/docs/support-component/routing.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function CustomHomePage() {
4242
</p>
4343

4444
<button
45-
className="rounded-xl border px-4 py-3 text-left"
45+
className="border px-4 py-3 text-left"
4646
onClick={() =>
4747
navigate({
4848
page: "CONVERSATION",
@@ -81,7 +81,7 @@ import { Support } from "@cossistant/react";
8181

8282
export default function SupportPanel() {
8383
return (
84-
<div className="h-[560px] overflow-hidden rounded-[24px] border">
84+
<div className="h-[560px] overflow-hidden border">
8585
<Support mode="responsive" />
8686
</div>
8787
);
@@ -108,7 +108,7 @@ export default function App() {
108108
<button type="button">Compose support</button>
109109
</Support.Trigger>
110110

111-
<Support.Content className="rounded-3xl border shadow-2xl">
111+
<Support.Content className="border shadow-2xl">
112112
<Support.Router>
113113
<Support.Page component={LaunchChecklistPage} name="HOME" />
114114
</Support.Router>

apps/web/content/docs/support-component/theme.mdx

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ search:
1414
- --co-theme-radius
1515
---
1616

17+
export function ColorSwatch({ value, label = value }) {
18+
return (
19+
<span
20+
aria-label={label}
21+
className="inline-block size-4 border border-dashed border-border align-middle"
22+
style={{ background: value }}
23+
title={label}
24+
/>
25+
);
26+
}
27+
1728
You can make the widget feel like part of your product without replacing any components.
1829

1930
## Use this when
@@ -33,7 +44,7 @@ Set the widget tokens once and keep the default UI.
3344
--co-theme-background: #ffffff;
3445
--co-theme-foreground: #111827;
3546
--co-theme-border: #e5e7eb;
36-
--co-theme-radius: 18px;
47+
--co-theme-radius: 0px;
3748
}
3849
```
3950

@@ -79,23 +90,46 @@ This keeps the widget aligned with the rest of your app instead of inventing a s
7990

8091
## Core token reference
8192

82-
| Variable | Default (Light) | Default (Dark) |
83-
| ------------------------------- | ------------------ | ------------------ |
84-
| `--co-theme-background` | `oklch(99% 0 0)` | `oklch(15.5% 0 0)` |
85-
| `--co-theme-foreground` | `oklch(14.5% 0 0)` | `oklch(98.5% 0 0)` |
86-
| `--co-theme-primary` | `oklch(20.5% 0 0)` | `oklch(98.5% 0 0)` |
87-
| `--co-theme-primary-foreground` | `oklch(98.5% 0 0)` | `oklch(20.5% 0 0)` |
88-
| `--co-theme-border` | `oklch(92.2% 0 0)` | `oklch(26.9% 0 0)` |
89-
| `--co-theme-muted` | Color-mixed | Color-mixed |
90-
| `--co-theme-muted-foreground` | Color-mixed | Color-mixed |
91-
| `--co-theme-radius` | `0.375rem` | `0.375rem` |
93+
| Variable | Light preview | Default (Light) | Dark preview | Default (Dark) |
94+
| ------------------------------- | --------------------------------------------------------- | ------------------ | --------------------------------------------------------- | ------------------ |
95+
| `--co-theme-background` | <ColorSwatch value="oklch(99% 0 0)" /> | `oklch(99% 0 0)` | <ColorSwatch value="oklch(15.5% 0 0)" /> | `oklch(15.5% 0 0)` |
96+
| `--co-theme-foreground` | <ColorSwatch value="oklch(20.5% 0 0)" /> | `oklch(20.5% 0 0)` | <ColorSwatch value="oklch(95% 0 0)" /> | `oklch(95% 0 0)` |
97+
| `--co-theme-primary` | <ColorSwatch value="oklch(14.5% 0 0)" /> | `oklch(14.5% 0 0)` | <ColorSwatch value="oklch(98.5% 0 0)" /> | `oklch(98.5% 0 0)` |
98+
| `--co-theme-primary-foreground` | <ColorSwatch value="oklch(98.5% 0 0)" /> | `oklch(98.5% 0 0)` | <ColorSwatch value="oklch(14.5% 0 0)" /> | `oklch(14.5% 0 0)` |
99+
| `--co-theme-border` | <ColorSwatch value="oklch(92.2% 0 0)" /> | `oklch(92.2% 0 0)` | <ColorSwatch value="oklch(26.9% 0 0)" /> | `oklch(26.9% 0 0)` |
100+
| `--co-theme-muted` | <ColorSwatch value="color-mix(in oklch, oklch(99% 0 0) 85%, oklch(20.5% 0 0))" /> | Color-mixed | <ColorSwatch value="color-mix(in oklch, oklch(15.5% 0 0) 55%, oklch(95% 0 0))" /> | Color-mixed |
101+
| `--co-theme-muted-foreground` | <ColorSwatch value="color-mix(in oklch, oklch(20.5% 0 0) 70%, white)" /> | Color-mixed | <ColorSwatch value="color-mix(in oklch, oklch(95% 0 0) 65%, white)" /> | Color-mixed |
102+
| `--co-theme-radius` | - | `0.375rem` | - | `0.375rem` |
92103

93104
## Extra tokens
94105

95106
Use these when the core tokens are not enough:
96107

97-
- Status colors: `--co-theme-destructive`, `--co-theme-success`, `--co-theme-warning`, `--co-theme-neutral`
98-
- Avatar accents: `--co-theme-pink`, `--co-theme-yellow`, `--co-theme-blue`, `--co-theme-orange`
99-
- Background shades: `--co-theme-background-50`, `--co-theme-background-100`, `--co-theme-background-200`, `--co-theme-background-300`
108+
### Status colors
109+
110+
| Variable | Light preview | Default (Light) | Dark preview | Default (Dark) |
111+
| ------------------------ | ------------------------------------------ | ------------------------ | ------------------------------------------ | ------------------------- |
112+
| `--co-theme-destructive` | <ColorSwatch value="oklch(57.7% 0.245 27.325)" /> | `oklch(57.7% 0.245 27.325)` | <ColorSwatch value="oklch(39.6% 0.141 25.723)" /> | `oklch(39.6% 0.141 25.723)` |
113+
| `--co-theme-success` | <ColorSwatch value="oklch(71.7% 0.18 142)" /> | `oklch(71.7% 0.18 142)` | <ColorSwatch value="oklch(60% 0.15 142)" /> | `oklch(60% 0.15 142)` |
114+
| `--co-theme-warning` | <ColorSwatch value="oklch(86.4% 0.144 99)" /> | `oklch(86.4% 0.144 99)` | <ColorSwatch value="oklch(90.3% 0.111 99)" /> | `oklch(90.3% 0.111 99)` |
115+
| `--co-theme-neutral` | <ColorSwatch value="oklch(60.8% 0 0)" /> | `oklch(60.8% 0 0)` | <ColorSwatch value="oklch(50% 0 0)" /> | `oklch(50% 0 0)` |
116+
117+
### Avatar accents
118+
119+
| Variable | Light preview | Default (Light) | Dark preview | Default (Dark) |
120+
| ------------------- | ----------------------------------------- | ---------------------- | ----------------------------------------- | ---------------------- |
121+
| `--co-theme-pink` | <ColorSwatch value="oklch(76.3% 0.152 354)" /> | `oklch(76.3% 0.152 354)` | <ColorSwatch value="oklch(84.2% 0.109 354)" /> | `oklch(84.2% 0.109 354)` |
122+
| `--co-theme-yellow` | <ColorSwatch value="oklch(86.4% 0.144 99)" /> | `oklch(86.4% 0.144 99)` | <ColorSwatch value="oklch(90.3% 0.111 99)" /> | `oklch(90.3% 0.111 99)` |
123+
| `--co-theme-blue` | <ColorSwatch value="oklch(72.5% 0.132 241)" /> | `oklch(72.5% 0.132 241)` | <ColorSwatch value="oklch(79.8% 0.089 241)" /> | `oklch(79.8% 0.089 241)` |
124+
| `--co-theme-orange` | <ColorSwatch value="oklch(74.5% 0.166 50)" /> | `oklch(74.5% 0.166 50)` | <ColorSwatch value="oklch(68.2% 0.194 50)" /> | `oklch(68.2% 0.194 50)` |
125+
126+
### Background shades
127+
128+
| Variable | Light preview | Default (Light) | Dark preview | Default (Dark) |
129+
| --------------------------- | ------------- | --------------- | ------------ | -------------- |
130+
| `--co-theme-background-50` | <ColorSwatch value="color-mix(in oklch, oklch(99% 0 0) 98%, oklch(20.5% 0 0))" /> | Color-mixed | <ColorSwatch value="color-mix(in oklch, oklch(15.5% 0 0) 98%, oklch(95% 0 0))" /> | Color-mixed |
131+
| `--co-theme-background-100` | <ColorSwatch value="color-mix(in oklch, oklch(99% 0 0) 97%, oklch(20.5% 0 0))" /> | Color-mixed | <ColorSwatch value="color-mix(in oklch, oklch(15.5% 0 0) 96%, oklch(95% 0 0))" /> | Color-mixed |
132+
| `--co-theme-background-200` | <ColorSwatch value="color-mix(in oklch, oklch(99% 0 0) 96%, oklch(20.5% 0 0))" /> | Color-mixed | <ColorSwatch value="color-mix(in oklch, oklch(15.5% 0 0) 94%, oklch(95% 0 0))" /> | Color-mixed |
133+
| `--co-theme-background-300` | <ColorSwatch value="color-mix(in oklch, oklch(99% 0 0) 95%, oklch(20.5% 0 0))" /> | Color-mixed | <ColorSwatch value="color-mix(in oklch, oklch(15.5% 0 0) 92%, oklch(95% 0 0))" /> | Color-mixed |
100134

101135
The background shades are derived from your base colors with `color-mix()` unless you override them directly.

apps/web/src/app/(dashboard)/[websiteSlug]/settings/team/team-settings-client.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ export function TeamSettingsClient({
408408
<div className="divide-y divide-primary/10">
409409
{settings.members.map((member) => {
410410
const memberDisplay = resolveDashboardHumanAgentDisplay({
411+
email: member.email ?? null,
411412
id: member.userId,
412413
name: member.name,
413414
});

apps/web/src/app/(lander-docs)/components/benefits/human-ai.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ const availableHumanAgents: AvailableHumanAgent[] = [
218218
{
219219
id: "anthony",
220220
name: "Anthony",
221+
email: "anthony@example.com",
221222
image: anthonyAvatar,
222223
lastSeenAt: new Date().toISOString(),
223224
},

apps/web/src/app/(lander-docs)/components/docs/component-preview-tabs.test.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { renderToStaticMarkup } from "react-dom/server";
44
import { DocsComponentPreviewTabs } from "./component-preview-tabs";
55

66
describe("DocsComponentPreviewTabs", () => {
7-
it("renders a square docs frame without a divider under the tabs", () => {
7+
it("renders tabs above a square docs frame without a tab divider", () => {
88
const html = renderToStaticMarkup(
99
<DocsComponentPreviewTabs
1010
component={<div>Preview</div>}
@@ -13,12 +13,16 @@ describe("DocsComponentPreviewTabs", () => {
1313
);
1414

1515
expect(html).toContain("mt-6 w-full min-w-0");
16+
expect(html).toContain('data-slot="docs-component-preview-tabs"');
1617
expect(html).toContain('data-slot="docs-component-preview-frame"');
1718
expect(html).toContain(
1819
"overflow-auto overscroll-contain bg-background px-4 py-6 md:max-h-[640px] dark:bg-background-100"
1920
);
20-
expect(html).toContain("px-4 pt-4 pb-3");
21-
expect(html).not.toContain("rounded-[24px]");
21+
expect(
22+
html.indexOf('data-slot="docs-component-preview-tabs"')
23+
).toBeLessThan(html.indexOf('data-slot="docs-component-preview-frame"'));
24+
expect(html).not.toContain("px-4 pt-4 pb-3");
25+
expect(html).not.toContain("rounded");
2226
expect(html).not.toContain("border-b border-dashed");
2327
});
2428
});

0 commit comments

Comments
 (0)