mirror of
https://github.com/block/buzz.git
synced 2026-11-13 12:51:34 +00:00
## Summary Fixes two instances of the same dead-truncate pattern in the desktop app, where a flex item's implicit `min-width: auto` prevented `truncate` from engaging, so long text painted over adjacent controls instead of ellipsizing: - **Inbox detail title** (`InboxDetailPane.tsx`): the clickable context-title button sized to its text instead of shrinking with the pane, overlapping the header controls (open-in-channel, members, huddle, more menu). Fixed by adding `max-w-full`. - **Message author names** (`MessageHeader.tsx` / `UserProfilePopover.tsx`): the `UserProfilePopover` inline-flex trigger wrapper refused to shrink below the name's nowrap width, running long author names under the hover action bar and off the pane edge. Fixed by adding a `triggerClassName` prop to `UserProfilePopover` and passing `min-w-0 max-w-full` at the author call site. Two other suspected instances (project file breadcrumb, drafts pane title) were stress-tested and already truncate correctly — no change. ### Related issue N/A — none found. ### Testing - New Playwright regression tests for both fixes (`inbox-title-overlap.spec.ts`, `message-author-overlap.spec.ts`, registered in the smoke project), each proven to discriminate: they fail with the fix reverted (real measured overlap) and assert the ellipsis actually engages with non-zero title width, so they can't pass vacuously. - Typecheck, lint, and full desktop unit suite green (pre-push hooks); full desktop e2e smoke suite run earlier: 1402 passed, 3 pre-existing unrelated failures (each fails identically with the fix reverted). **Inbox title — before** (long title paints under the header controls):  **Inbox title — after** (truncates with ellipsis, controls stay clear):  **Author name — before** (long name runs past the header row edge):  **Author name — after** (clean cutoff):  🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: cynfria <yescynthia@gmail.com> Signed-off-by: Tree Trunks <6ba22921d9dc2ad0aa6ecdf63787ddd24726e266d866da31af69f2e4e146ace5@buzz.block.builderlab.xyz> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Tree Trunks <6ba22921d9dc2ad0aa6ecdf63787ddd24726e266d866da31af69f2e4e146ace5@buzz.block.builderlab.xyz>
148 lines
3.8 KiB
TypeScript
148 lines
3.8 KiB
TypeScript
import * as React from "react";
|
|
|
|
import { AgentManagementMarker } from "@/features/agents/ui/OtherSetupAgentMarker";
|
|
import { UserProfilePopover } from "@/features/profile/ui/UserProfilePopover";
|
|
|
|
import { cn } from "@/shared/lib/cn";
|
|
|
|
type MessageHeaderRowProps = {
|
|
children: React.ReactNode;
|
|
className?: string;
|
|
};
|
|
|
|
export function MessageHeaderRow({
|
|
children,
|
|
className,
|
|
}: MessageHeaderRowProps) {
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"flex min-w-0 flex-wrap items-baseline gap-x-1.5 gap-y-0 leading-message-author",
|
|
className,
|
|
)}
|
|
data-testid="message-header"
|
|
>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Divider between two pieces of message-header metadata.
|
|
*
|
|
* The header runs several independent facts together on one line, and without a
|
|
* divider they read as one phrase: an agent message came out as "managed by You
|
|
* 9:53 AM". A middot is what the rest of the app already uses for this
|
|
* (`MessageThreadSummaryRow`, project rows, the mention list).
|
|
*
|
|
* `aria-hidden` because the divider is punctuation for the eye only — the header
|
|
* already reads as separate nodes to a screen reader, and `MessageAgentOwner`
|
|
* supplies its own "Agent managed by" label.
|
|
*
|
|
* No horizontal margin: `MessageHeaderRow` is a flex row with `gap-x-1.5`, so
|
|
* spacing comes from the container. Adding margin here double-spaces it.
|
|
*/
|
|
export function MessageMetaSeparator() {
|
|
return (
|
|
<span aria-hidden="true" className="text-xs text-muted-foreground/40">
|
|
·
|
|
</span>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Renders divider-separated header metadata segments in order, skipping empty
|
|
* slots. Each divider is grouped with the segment it precedes so the two wrap
|
|
* together — as loose siblings in a flex-wrap row, a divider can end up alone
|
|
* at the start of the second line.
|
|
*/
|
|
export function MessageMetaSegments({
|
|
segments,
|
|
}: {
|
|
segments: ReadonlyArray<{ key: string; node: React.ReactNode }>;
|
|
}) {
|
|
const present = segments.filter((slot) => Boolean(slot.node));
|
|
return (
|
|
<>
|
|
{present.map(({ key, node }, index) =>
|
|
index === 0 ? (
|
|
<React.Fragment key={key}>{node}</React.Fragment>
|
|
) : (
|
|
<span
|
|
className="inline-flex min-w-0 items-baseline gap-x-1.5"
|
|
key={key}
|
|
>
|
|
<MessageMetaSeparator />
|
|
{node}
|
|
</span>
|
|
),
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
|
|
type MessageAuthorTextProps = {
|
|
as?: "div" | "h3" | "span";
|
|
children: React.ReactNode;
|
|
className?: string;
|
|
hoverUnderline?: boolean;
|
|
};
|
|
|
|
export function MessageAuthorText({
|
|
as: Component = "span",
|
|
children,
|
|
className,
|
|
hoverUnderline = false,
|
|
}: MessageAuthorTextProps) {
|
|
return (
|
|
<Component
|
|
className={cn(
|
|
"truncate text-message font-semibold leading-message-author tracking-normal",
|
|
hoverUnderline && "hover:underline",
|
|
className,
|
|
)}
|
|
data-testid="message-author"
|
|
>
|
|
{children}
|
|
</Component>
|
|
);
|
|
}
|
|
|
|
/** Author navigation and provenance always refer to the same exact identity. */
|
|
export function MessageAuthorIdentity({
|
|
pubkey,
|
|
ownerPubkey,
|
|
role,
|
|
displayName,
|
|
children,
|
|
}: {
|
|
pubkey?: string | null;
|
|
ownerPubkey?: string | null;
|
|
role?: string;
|
|
displayName: string;
|
|
children: React.ReactNode;
|
|
}) {
|
|
return (
|
|
<>
|
|
{pubkey ? (
|
|
<UserProfilePopover
|
|
pubkey={pubkey}
|
|
role={role}
|
|
botIdenticonValue={displayName}
|
|
triggerClassName="min-w-0 max-w-full"
|
|
>
|
|
<button
|
|
className="truncate rounded leading-message-author focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
|
|
type="button"
|
|
>
|
|
{children}
|
|
</button>
|
|
</UserProfilePopover>
|
|
) : (
|
|
children
|
|
)}
|
|
<AgentManagementMarker pubkey={pubkey} ownerPubkey={ownerPubkey} />
|
|
</>
|
|
);
|
|
}
|