Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1c0e87c
feat(design-system): standardize forms and document control states
mahanti Sep 23, 2026
dc863e0
fix(design-system): refine form feedback and control styling
mahanti Sep 23, 2026
835200d
fix(forms): adopt shared controls and field error feedback
mahanti Sep 23, 2026
7786d9e
Merge remote-tracking branch 'origin/main' into codex/forms-system
mahanti Sep 23, 2026
aa182ac
test: update browser journeys for shared form controls
mahanti Sep 23, 2026
e19db12
Unify menus and popovers with shared floating surfaces
mahanti Sep 23, 2026
75fd7fb
Refine composer pickers and compact action surfaces
mahanti Sep 24, 2026
bdad085
Align composer admission tests with shared choice labels
mahanti Sep 24, 2026
c9bfde8
Merge main and repair picker integration and CI regressions
mahanti Sep 24, 2026
49ed02a
test: preserve merged picker and agent-choice contracts
mahanti Sep 24, 2026
9b7d125
test: model read publication in agent profile activity journey
mahanti Sep 24, 2026
aafc3a0
test: wait for message menu lifecycle before Escape
mahanti Sep 24, 2026
cebd07a
Merge main and preserve picker naming and activity coverage
mahanti Sep 24, 2026
825e5d6
Restore agent history disclosure and align search focus styling
mahanti Sep 24, 2026
497ad34
Complete session disclosure fixture metadata
mahanti Sep 24, 2026
7fc89b3
Wait for profile focus and timeline layout before read dwell
mahanti Sep 24, 2026
fe60f88
Merge current main and retain context-menu session creation
mahanti Sep 24, 2026
06f7181
Separate activity hover dismissal from keyboard reopening
mahanti Sep 24, 2026
0c21ae5
Merge main and preserve profile read-state coverage
mahanti Sep 24, 2026
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
2 changes: 2 additions & 0 deletions scripts/design-system/check-contrast.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ const TEXT_ROLES = [
* every fill it can actually sit on, and hover is one of them.
*/
const PAIRS = [
["--text-standard", "--affordance-floating-hover"],
["--text-danger", "--affordance-floating-hover"],
["--text-inverse", "--surface-inverse"],
["--text-link", "--affordance-link-hover"],
...["subtle", "subtle-hover", "subtle-pressed"].map((state) => [
Expand Down
4 changes: 4 additions & 0 deletions scripts/design-system/check-type.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ const RETIRED_ROLES = new Map([
* wrong. Fix the system at that point rather than adding a tenth override.
*/
const OVERRIDES = new Map([
[
"styles/search-field.css:font-size:",
"The capsule recipe forwards the text-body-sm role into Emoji Mart's shadow root, where Tailwind utilities cannot reach.",
],
[
"styles/flex-workspace.css:font-size:",
"FlexLayout's public theme variable forwards the existing text-body role; no new text size is authored.",
Expand Down
90 changes: 44 additions & 46 deletions src/bundled/agents/AgentCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { useRef, type ReactNode } from "react";
import { Menu } from "@base-ui/react/menu";
import {
MenuRoot,
MenuTrigger,
MenuPopup,
MenuItem,
MenuNote,
} from "../../shared/design-system/ui/Menu";
import { ChoiceRow } from "../../shared/design-system/ui/ChoiceRow";
import {
DotsThreeIcon,
UsersIcon,
Expand Down Expand Up @@ -45,8 +52,8 @@ export function AgentCard({
>
{onEdit && (
<div className="absolute right-2 top-2">
<Menu.Root>
<Menu.Trigger
<MenuRoot>
<MenuTrigger
ref={trigger}
render={
<IconButton
Expand All @@ -56,57 +63,48 @@ export function AgentCard({
/>
}
/>
<Menu.Portal>
<Menu.Positioner align="end" sideOffset={4}>
<Menu.Popup
data-buzz-ui=""
className="min-w-36 max-w-[min(28rem,calc(100vw-2rem))] rounded-xl border border-primary bg-float p-1 text-body text-primary shadow-lg"
>
{editable.length ? (
editable.map((agent) => (
<Menu.Item
key={agent.id}
className="cursor-pointer rounded-lg px-3 py-2 outline-none data-[highlighted]:bg-hover"
onClick={() => {
// The menu item unmounts; return from the dialog to the card.
trigger.current?.focus();
onEdit(agent, picture);
}}
>
{editable.length === 1 ? (
"Edit"
) : (
<MenuPopup align="end" size="wide">
{editable.length ? (
editable.map((agent) => (
<MenuItem
key={agent.id}
onClick={() => {
// The menu item unmounts; return from the dialog to the card.
trigger.current?.focus();
onEdit(agent, picture);
}}
>
{editable.length === 1 ? (
"Edit"
) : (
<ChoiceRow
label={`Edit ${identityLabel(agent)}`}
description={
<>
Edit {identityLabel(agent)}
<span className="block break-all text-body-sm text-secondary">
{agent.relayUrl}
</span>
<span className="block break-all text-mono-sm text-secondary">
{agent.pubkey}
</span>
</>
)}
</Menu.Item>
))
) : (
<>
<Menu.Item
disabled
className="rounded-lg px-3 py-2 text-disabled"
>
Edit
</Menu.Item>
<p className="m-0 max-w-64 px-3 py-2 text-body-sm text-secondary">
{identities.length
? "Import this identity to edit in Foundation."
: "No linked identity to edit."}
</p>
</>
)}
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
}
/>
)}
</MenuItem>
))
) : (
<>
<MenuItem disabled>Edit</MenuItem>
<MenuNote>
{identities.length
? "Import this identity to edit in Foundation."
: "No linked identity to edit."}
</MenuNote>
</>
)}
</MenuPopup>
</MenuRoot>
</div>
)}
<div
Expand Down
66 changes: 66 additions & 0 deletions src/bundled/channels/ChannelActivityPopover.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// @vitest-environment jsdom
import "@testing-library/jest-dom/vitest";
import { cleanup, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { afterEach, expect, test, vi } from "vitest";
import { ChannelActivityPopover } from "./ChannelActivityPopover";
import { Button } from "../../shared/design-system/ui/Button";
import type { RelaySession } from "../../features/relay/session";
import type {
ThreadActivityItem,
ThreadActivitySnapshot,
} from "../../features/relay/unread";

afterEach(cleanup);

test("activity keeps profile loading lazy, explains stale results and opens the selected thread", async () => {
const user = userEvent.setup();
const item: ThreadActivityItem = {
channelId: "studio",
rootId: "thread",
latestMessageId: "message",
authorId: "alex",
createdAt: 1,
preview: "Please review the designs",
unreadCount: 2,
};
const snapshot: ThreadActivitySnapshot = {
channelId: "studio",
items: [item],
coverage: "observed",
freshness: "stale",
};
const profiles = new Map([["alex", { name: "Alex" }]]);
const ensure = vi.fn(async () => {});
const open = vi.fn();
const session = {
unread: { activity: () => snapshot, subscribeActivity: () => () => {} },
profiles: { snapshot: () => profiles, subscribe: () => () => {}, ensure },
} as unknown as RelaySession;
render(
<ChannelActivityPopover
session={session}
channelId="studio"
channelName="Studio"
trigger={<Button>Activity</Button>}
onOpenThread={open}
/>,
);
expect(ensure).not.toHaveBeenCalled();
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
await user.click(screen.getByRole("button", { name: "Activity" }));
expect(
await screen.findByRole("dialog", { name: "Activity in Studio" }),
).toBeVisible();
expect(ensure).toHaveBeenCalledExactlyOnceWith(["alex"], "background");
expect(screen.getByText("May be out of date")).toBeVisible();
await user.click(
screen.getByRole("button", {
name: "Open unread thread from Alex: Please review the designs",
}),
);
expect(open).toHaveBeenCalledExactlyOnceWith(item);
await waitFor(() =>
expect(screen.queryByRole("dialog")).not.toBeInTheDocument(),
);
});
69 changes: 33 additions & 36 deletions src/bundled/channels/ChannelActivityPopover.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { useChannelIdentityNames } from "../../features/identity-names/react";
import { NavigationItem } from "../../shared/design-system/ui/NavigationItem";
import { Popover } from "@base-ui/react/popover";
import {
PopoverRoot,
PopoverTrigger,
PopoverPopup,
} from "../../shared/design-system/ui/Popover";
import {
useCallback,
useMemo,
Expand Down Expand Up @@ -104,7 +108,7 @@ export function ChannelActivityPopover({
if (!items.length) return trigger;
const stale = snapshot.freshness === "stale";
return (
<Popover.Root
<PopoverRoot
modal={false}
open={open}
onOpenChange={(next) => {
Expand All @@ -118,44 +122,37 @@ export function ChannelActivityPopover({
.catch(() => {});
}}
>
<Popover.Trigger
<PopoverTrigger
render={trigger}
openOnHover
delay={250}
closeDelay={150}
/>
<Popover.Portal>
<Popover.Positioner
side="right"
align="start"
sideOffset={6}
collisionPadding={8}
>
<Popover.Popup
className={styles.activityPopover}
aria-label={`Activity in ${channelName}`}
>
{stale && (
<p className={styles.activityStale}>May be out of date</p>
)}
{open && (
<div className={styles.activityList}>
{items.map((item) => (
<ActivityRow
key={item.rootId}
item={item}
session={session}
onOpen={(selected) => {
setOpen(false);
onOpenThread(selected);
}}
/>
))}
</div>
)}
</Popover.Popup>
</Popover.Positioner>
</Popover.Portal>
</Popover.Root>
<PopoverPopup
side="right"
align="start"
sideOffset={6}
size="wide"
padding="list"
aria-label={`Activity in ${channelName}`}
>
{stale && <p className={styles.activityStale}>May be out of date</p>}
{open && (
<div className={styles.activityList}>
{items.map((item) => (
<ActivityRow
key={item.rootId}
item={item}
session={session}
onOpen={(selected) => {
setOpen(false);
onOpenThread(selected);
}}
/>
))}
</div>
)}
</PopoverPopup>
</PopoverRoot>
);
}
15 changes: 0 additions & 15 deletions src/bundled/channels/Channels.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -456,21 +456,6 @@ html[data-keyboard-navigation]
.threadActivityDot {
cursor: help;
}
.activityPopover {
@apply popover-surface;
width: min(360px, calc(100vw - 24px));
max-height: min(420px, calc(100vh - 24px));
overflow: hidden;
transform-origin: var(--transform-origin);
transition:
opacity 120ms ease-out,
transform 120ms ease-out;
}
.activityPopover[data-starting-style],
.activityPopover[data-ending-style] {
opacity: 0;
transform: scale(0.98);
}
.activityStale {
margin: 0;
padding: var(--space-1h) var(--space-3);
Expand Down
5 changes: 5 additions & 0 deletions src/bundled/composer/lab/fixture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ const rawSession = {
reply: () => "preview-reply",
},
typing: { snapshot: () => emptyList, subscribe: empty },
names: {
snapshot: () => 0,
subscribe: empty,
resolve: (_pubkey: string, fallback: string) => fallback,
},
profiles: {
snapshot: () => profiles,
subscribe: empty,
Expand Down
Loading
Loading