fix(platform): Wallet fixes (#11304)

### Changes 🏗️

- Unmask for Sentry:
  - Agent name&creator on onboarding cards
  - Edge paths
  - Block I/O names
- Prevent firing `onClick` when onboarding agents are loading
- Prevent confetti on null elements and top-left corner
- Fix tooltip on Wallet hover
- Fix `0` appearing in place of notification dot on the Wallet button

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  - [x] Onboarding works and can be completed
  - [x] Wallet confetti works properly
  - [x] Tooltip works
This commit is contained in:
Krzysztof Czerwinski
2025-11-05 23:17:43 +09:00
committed by GitHub
parent 2f87e13d17
commit 979826f559
6 changed files with 28 additions and 30 deletions

View File

@@ -24,10 +24,10 @@ export function SelectedAgentCard(props: Props) {
{/* Right content */}
<div className="ml-3 flex flex-1 flex-col">
<div className="mb-2 flex flex-col items-start">
<span className="w-[292px] truncate font-sans text-[14px] font-medium leading-tight text-zinc-800">
<span className="data-sentry-unmask w-[292px] truncate font-sans text-[14px] font-medium leading-tight text-zinc-800">
{props.storeAgent.agent_name}
</span>
<span className="font-norma w-[292px] truncate font-sans text-xs text-zinc-600">
<span className="data-sentry-unmask font-norma w-[292px] truncate font-sans text-xs text-zinc-600">
by {props.storeAgent.creator}
</span>
</div>

View File

@@ -21,7 +21,6 @@ export default function OnboardingAgentCard({
"relative animate-pulse",
"h-[394px] w-[368px] rounded-[20px] border border-transparent bg-zinc-200",
)}
onClick={onClick}
/>
);
}
@@ -67,12 +66,12 @@ export default function OnboardingAgentCard({
{/* Text content wrapper */}
<div>
{/* Title - 2 lines max */}
<p className="text-md line-clamp-2 max-h-[50px] font-sans text-base font-medium leading-normal text-zinc-800">
<p className="data-sentry-unmask text-md line-clamp-2 max-h-[50px] font-sans text-base font-medium leading-normal text-zinc-800">
{agent_name}
</p>
{/* Author - single line with truncate */}
<p className="truncate text-sm font-normal leading-normal text-zinc-600">
<p className="data-sentry-unmask truncate text-sm font-normal leading-normal text-zinc-600">
by {creator}
</p>

View File

@@ -176,14 +176,14 @@ export function CustomEdge({
<BaseEdge
path={svgPath}
markerEnd={markerEnd}
className={`transition-all duration-200 ${data?.isStatic ? "[stroke-dasharray:5_3]" : "[stroke-dasharray:0]"} [stroke-width:${data?.isStatic ? 2.5 : 2}px] hover:[stroke-width:${data?.isStatic ? 3.5 : 3}px] ${selected ? `[stroke:${data?.edgeColor ?? "#555555"}]` : `[stroke:${data?.edgeColor ?? "#555555"}80] hover:[stroke:${data?.edgeColor ?? "#555555"}]`}`}
className={`data-sentry-unmask transition-all duration-200 ${data?.isStatic ? "[stroke-dasharray:5_3]" : "[stroke-dasharray:0]"} [stroke-width:${data?.isStatic ? 2.5 : 2}px] hover:[stroke-width:${data?.isStatic ? 3.5 : 3}px] ${selected ? `[stroke:${data?.edgeColor ?? "#555555"}]` : `[stroke:${data?.edgeColor ?? "#555555"}80] hover:[stroke:${data?.edgeColor ?? "#555555"}]`}`}
/>
<path
d={svgPath}
fill="none"
strokeOpacity={0}
strokeWidth={20}
className="react-flow__edge-interaction"
className="data-sentry-unmask react-flow__edge-interaction"
/>
<EdgeLabelRenderer>
<div

View File

@@ -58,14 +58,14 @@ const NodeHandle: FC<HandleProps> = ({
<div className="flex flex-grow flex-row">
<span
className={cn(
"text-m green flex items-end pr-2 text-gray-900 dark:text-gray-100",
"data-sentry-unmask text-m green flex items-end pr-2 text-gray-900 dark:text-gray-100",
className,
)}
>
{title || schema.title || beautifyString(keyName.toLowerCase())}
{isRequired ? "*" : ""}
</span>
<span className={`${typeClass} flex items-end`}>
<span className={`${typeClass} data-sentry-unmask flex items-end`}>
({TYPE_NAME[schema.type as keyof typeof TYPE_NAME] || "any"})
</span>
</div>

View File

@@ -216,7 +216,7 @@ export function Wallet() {
if (typeof credits !== "number") return;
// Open once for first-time users
if (state && state.walletShown === false) {
setWalletOpen(true);
requestAnimationFrame(() => setWalletOpen(true));
// Mark as shown so it won't reopen on every reload
updateState({ walletShown: true });
return;
@@ -227,7 +227,7 @@ export function Wallet() {
credits > lastSeenCredits &&
walletOpen === false
) {
setWalletOpen(true);
requestAnimationFrame(() => setWalletOpen(true));
}
}, [credits, lastSeenCredits, state?.walletShown, updateState, walletOpen]);
@@ -260,28 +260,28 @@ export function Wallet() {
// Otherwise, we need to set the new prevCompletedCount
setPrevCompletedCount(completedCount);
// If there was no previous count, we don't show confetti
if (prevCompletedCount === null) {
if (prevCompletedCount === null || !walletRef.current) {
return;
}
// And emit confetti
if (walletRef.current) {
setTimeout(() => {
fetchCredits();
if (!walletRef.current) {
return;
}
// Fix confetti appearing in the top left corner
const rect = walletRef.current.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) {
return;
}
setTimeout(() => {
fetchCredits();
party.confetti(walletRef.current!, {
count: 30,
spread: 120,
shapes: ["square", "circle"],
size: party.variation.range(1, 2),
speed: party.variation.range(200, 300),
modules: [fadeOut],
});
}, 800);
}
party.confetti(walletRef.current, {
count: 30,
spread: 120,
shapes: ["square", "circle"],
size: party.variation.range(1, 2),
speed: party.variation.range(200, 300),
modules: [fadeOut],
});
}, 800);
}, [
state?.completedSteps,
state?.notified,
@@ -328,9 +328,7 @@ export function Wallet() {
<div className="relative inline-block">
<button
ref={walletRef}
className={cn(
"relative flex flex-nowrap items-center gap-2 rounded-md bg-zinc-50 px-3 py-2 text-sm",
)}
className="group relative flex flex-nowrap items-center gap-2 rounded-md bg-zinc-50 px-3 py-2 text-sm"
onClick={onWalletOpen}
>
<WalletIcon size={20} className="inline-block md:hidden" />
@@ -339,7 +337,7 @@ export function Wallet() {
<span className="text-sm font-semibold">
{formatCredits(credits)}
</span>
{completedCount && completedCount < totalCount && (
{completedCount !== null && completedCount < totalCount && (
<span className="absolute right-1 top-1 h-2 w-2 rounded-full bg-violet-600"></span>
)}
<div className="absolute bottom-[-2.5rem] left-1/2 z-50 hidden -translate-x-1/2 transform whitespace-nowrap rounded-small bg-white px-4 py-2 shadow-md group-hover:block">

View File

@@ -72,6 +72,7 @@ export function TaskGroups({ groups }: Props) {
const delayConfetti = useCallback((el: HTMLDivElement, count: number) => {
setTimeout(() => {
if (!el) return;
party.confetti(el, {
count,
spread: 90,