mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
fix(native-chat): give each animation a reduced-motion state (#26276)
Spinners swap to a still hourglass instead of freezing; chevrons, the rail and popovers stop moving.
This commit is contained in:
+1
-1
@@ -264,7 +264,7 @@ Use this rubric when reviewing any Orca IDE screen, screenshot, or prototype. A
|
||||
- **Layout density:** avoid jamming controls together. Preserve breathing room around the primary workflow, reduce competing buttons, and keep toolbar groups visually distinct. Dense screens are acceptable only when grouping, alignment, and hierarchy make scanning faster.
|
||||
- **Cards and containers:** cards must be visually distinct from their parent surface through the existing `card`/`border` treatment. Avoid nesting cards inside cards. If a section is not a repeated item, modal, or framed tool, consider an unframed layout or full-width band instead.
|
||||
- **Side-by-side layouts:** default to row-by-row layouts for complex workflows because they are easier to align and scan. Use side-by-side layouts only when space is constrained or comparison is the point, then polish column widths, baselines, and wrapping states carefully.
|
||||
- **Animation:** use subtle animation to soften expanding/collapsing content and prevent jumpy layout changes. Animation should clarify continuity, not decorate. Respect reduced-motion settings.
|
||||
- **Animation:** use subtle animation to soften expanding/collapsing content and prevent jumpy layout changes. Animation should clarify continuity, not decorate. Respect reduced motion element by element: turn movement off with `motion-reduce:` (slides, rotations, size changes); give spinners, pulses and progress indicators a still state that still says "working" (`LoadingSpinner` does this for spinners) instead of freezing them; short opacity fades can stay.
|
||||
- **SSH and latency:** assume actions may run remotely. Disable submit controls immediately, delay visible loading feedback when appropriate, and keep focus stable while remote data arrives.
|
||||
|
||||
### 1. Match in-flight feedback to perceived duration
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import React from 'react'
|
||||
import { Hourglass, Loader2 } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
// Why: a spinner frozen by reduced motion reads as stuck, so the still state is
|
||||
// a different icon that says "working" without moving.
|
||||
export function LoadingSpinner({ className }: { className?: string }): React.JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<Loader2 aria-hidden="true" className={cn('animate-spin motion-reduce:hidden', className)} />
|
||||
<Hourglass aria-hidden="true" className={cn('hidden motion-reduce:block', className)} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { memo, useEffect, useRef } from 'react'
|
||||
import { Loader2, Package, RotateCcw } from 'lucide-react'
|
||||
import { Package, RotateCcw } from 'lucide-react'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import { cn } from '@/lib/utils'
|
||||
import type { SkillSourceKind } from '../../../../shared/skills'
|
||||
@@ -7,6 +7,7 @@ import { FilenameFirstPath } from '@/components/file-path-cursor-tooltip'
|
||||
import { getFileTypeIcon } from '@/lib/file-type-icons'
|
||||
import type { ComposerAutocomplete, NativeChatPickerItem } from './native-chat-composer-state'
|
||||
import type { NativeChatMentionFiles } from './use-native-chat-mention-files'
|
||||
import { LoadingSpinner } from '@/components/LoadingSpinner'
|
||||
|
||||
export const NativeChatPickerMenu = memo(function NativeChatPickerMenu({
|
||||
autocomplete,
|
||||
@@ -72,7 +73,7 @@ export const NativeChatPickerMenu = memo(function NativeChatPickerMenu({
|
||||
{showSkillsHeading ? <PickerGroupHeading kind="skills" /> : null}
|
||||
{autocomplete.skillStatus === 'loading' ? (
|
||||
<PickerStatus>
|
||||
<Loader2 className="size-3.5 animate-spin" />
|
||||
<LoadingSpinner className="size-3.5" />
|
||||
{translate('components.native-chat.composer.loadingSkills', 'Loading skills...')}
|
||||
</PickerStatus>
|
||||
) : null}
|
||||
@@ -353,7 +354,7 @@ export const NativeChatMentionMenu = memo(function NativeChatMentionMenu({
|
||||
})}
|
||||
{statusText ? (
|
||||
<PickerStatus>
|
||||
{mention.loading ? <Loader2 className="size-3.5 animate-spin" /> : null}
|
||||
{mention.loading ? <LoadingSpinner className="size-3.5" /> : null}
|
||||
{statusText}
|
||||
</PickerStatus>
|
||||
) : null}
|
||||
|
||||
@@ -93,7 +93,7 @@ export function NativeChatAwaitingInputRow({
|
||||
<ChevronRight
|
||||
aria-hidden
|
||||
className={cn(
|
||||
'size-3.5 shrink-0 text-muted-foreground transition-all',
|
||||
'size-3.5 shrink-0 text-muted-foreground transition-all motion-reduce:transition-none',
|
||||
open
|
||||
? 'rotate-90'
|
||||
: 'can-hover:opacity-0 group-hover/ask-row:opacity-100 group-focus-visible/ask-row:opacity-100'
|
||||
|
||||
@@ -231,7 +231,7 @@ export function NativeChatBackgroundTasksStatus(props: {
|
||||
</span>
|
||||
<ChevronDown
|
||||
aria-hidden="true"
|
||||
className={`size-3 transition-transform ${expanded ? 'rotate-180' : ''}`}
|
||||
className={`size-3 transition-transform motion-reduce:transition-none ${expanded ? 'rotate-180' : ''}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -217,7 +217,7 @@ export function NativeChatDiffCard({
|
||||
{hasBody ? (
|
||||
<ChevronRight
|
||||
className={cn(
|
||||
'size-3.5 shrink-0 text-chat-foreground-faint transition-transform',
|
||||
'size-3.5 shrink-0 text-chat-foreground-faint transition-transform motion-reduce:transition-none',
|
||||
expanded && 'rotate-90'
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { FileText, Image as ImageIcon, ImageOff, Loader2, X } from 'lucide-react'
|
||||
import { FileText, Image as ImageIcon, ImageOff, X } from 'lucide-react'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogTitle } from '@/components/ui/dialog'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import { basename } from '@/lib/path'
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import type { NativeChatComposerImageAttachment } from './NativeChatComposerField'
|
||||
import { isAgentSessionAttachmentStorePath } from '../../../../shared/agent-session-attachments'
|
||||
import { chatImageAccess } from '@/lib/local-file-access'
|
||||
import { LoadingSpinner } from '@/components/LoadingSpinner'
|
||||
|
||||
type Props = {
|
||||
attachment: NativeChatComposerImageAttachment
|
||||
@@ -218,7 +219,7 @@ function NativeChatImageThumbnail({
|
||||
</button>
|
||||
{isPending ? (
|
||||
<span className="pointer-events-none absolute inset-0 flex items-center justify-center rounded-md bg-chat-canvas/50">
|
||||
<Loader2 className="size-4 animate-spin text-muted-foreground" />
|
||||
<LoadingSpinner className="size-4 text-muted-foreground" />
|
||||
</span>
|
||||
) : null}
|
||||
<RemoveAttachmentButton onRemove={() => onRemove(attachment.id)} />
|
||||
@@ -244,7 +245,7 @@ function NativeChatImageThumbnail({
|
||||
<div className="flex items-center gap-2 py-12 text-sm text-muted-foreground">
|
||||
{isPending ? (
|
||||
<>
|
||||
<Loader2 className="size-4 animate-spin" />
|
||||
<LoadingSpinner className="size-4" />
|
||||
{pendingLabel}
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Loader2 } from 'lucide-react'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import { LoadingSpinner } from '@/components/LoadingSpinner'
|
||||
|
||||
/** The structured chat's pane while its first read runs. No visible text, and a CSS animation
|
||||
* delay keeps it invisible unless the read lasts, so a quick read paints nothing. */
|
||||
@@ -11,7 +11,7 @@ export function NativeChatLoadingCue(): React.JSX.Element {
|
||||
data-native-chat-loading-cue="true"
|
||||
className="flex h-full w-full items-center justify-center animate-in fade-in delay-250 [--tw-animation-fill-mode:backwards]"
|
||||
>
|
||||
<Loader2 aria-hidden="true" className="size-5 animate-spin text-muted-foreground" />
|
||||
<LoadingSpinner className="size-5 text-muted-foreground" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
+2
-2
@@ -92,7 +92,7 @@ describe('NativeChatMessageList turn indicator', () => {
|
||||
const row = activity.closest('[data-native-chat-turn-activity]')
|
||||
const spinner = row?.querySelector('svg')
|
||||
expect(activity).not.toHaveClass('animate-pulse', 'animate-spin')
|
||||
expect(spinner).toHaveClass('size-4', 'animate-spin', 'motion-reduce:animate-none')
|
||||
expect(spinner).toHaveClass('size-4', 'animate-spin', 'motion-reduce:hidden')
|
||||
expect(row).toHaveAttribute('aria-live', 'polite')
|
||||
expect(screen.getByText('The answer is still streaming.').compareDocumentPosition(row!)).toBe(
|
||||
Node.DOCUMENT_POSITION_FOLLOWING
|
||||
@@ -137,7 +137,7 @@ describe('NativeChatMessageList turn indicator', () => {
|
||||
expect(activity).not.toHaveTextContent('pnpm test')
|
||||
const spinner = activity.closest('[data-native-chat-turn-activity]')?.querySelector('svg')
|
||||
expect(activity).not.toHaveClass('animate-pulse', 'animate-spin')
|
||||
expect(spinner).toHaveClass('animate-spin', 'motion-reduce:animate-none')
|
||||
expect(spinner).toHaveClass('animate-spin', 'motion-reduce:hidden')
|
||||
})
|
||||
|
||||
it('hides foreground turn activity without settling live tool state', () => {
|
||||
|
||||
@@ -194,9 +194,10 @@ export const NativeChatMessageRail = memo(function NativeChatMessageRail({
|
||||
<span
|
||||
aria-hidden
|
||||
className={cn(
|
||||
'h-[3px] rounded-full transition-[width,background-color] duration-150',
|
||||
'h-[3px] rounded-full transition-[width,background-color] duration-150 motion-reduce:transition-none',
|
||||
lit ? 'w-5 bg-foreground' : 'w-3 bg-foreground/50',
|
||||
item.id === pendingId && 'animate-pulse'
|
||||
item.id === pendingId &&
|
||||
'animate-pulse motion-reduce:animate-none motion-reduce:opacity-60'
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
|
||||
@@ -347,7 +347,12 @@ function QueuedMessageExpandToggle({
|
||||
aria-controls={controls}
|
||||
onClick={onToggle}
|
||||
>
|
||||
<ChevronDown className={cn('size-3.5 transition-transform', expanded && 'rotate-180')} />
|
||||
<ChevronDown
|
||||
className={cn(
|
||||
'size-3.5 transition-transform motion-reduce:transition-none',
|
||||
expanded && 'rotate-180'
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top" sideOffset={4}>
|
||||
|
||||
@@ -129,7 +129,7 @@ export function NativeChatSubagentEntries({
|
||||
<ChevronRight
|
||||
aria-hidden
|
||||
className={cn(
|
||||
'size-3.5 shrink-0 text-chat-foreground-faint transition-all',
|
||||
'size-3.5 shrink-0 text-chat-foreground-faint transition-all motion-reduce:transition-none',
|
||||
sectionOpen
|
||||
? 'rotate-90 opacity-100'
|
||||
: 'opacity-0 group-hover/subagent-entry:opacity-100'
|
||||
@@ -218,7 +218,7 @@ export function NativeChatSubagentRun({
|
||||
</span>
|
||||
<ChevronRight
|
||||
className={cn(
|
||||
'size-3.5 shrink-0 text-chat-foreground-faint transition-all',
|
||||
'size-3.5 shrink-0 text-chat-foreground-faint transition-all motion-reduce:transition-none',
|
||||
open ? 'rotate-90 opacity-100' : 'opacity-0 group-hover/subagent-run:opacity-100'
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -29,7 +29,10 @@ export function NativeChatSubagentSectionHead({
|
||||
>
|
||||
<ChevronRight
|
||||
aria-hidden
|
||||
className={cn('size-3.5 shrink-0 transition-transform', expanded && 'rotate-90')}
|
||||
className={cn(
|
||||
'size-3.5 shrink-0 transition-transform motion-reduce:transition-none',
|
||||
expanded && 'rotate-90'
|
||||
)}
|
||||
/>
|
||||
<Bot aria-hidden className="size-3.5 shrink-0" />
|
||||
{state === null ? null : <StatusDot state={state} pulsing={state === 'working'} />}
|
||||
|
||||
@@ -184,7 +184,7 @@ export function NativeChatToolLine({
|
||||
{hasDetail ? (
|
||||
<ChevronRight
|
||||
className={cn(
|
||||
'size-3.5 shrink-0 text-chat-foreground-faint transition-transform',
|
||||
'size-3.5 shrink-0 text-chat-foreground-faint transition-transform motion-reduce:transition-none',
|
||||
expanded && 'rotate-90'
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -327,7 +327,9 @@ export function ProviderFrameRow({
|
||||
return (
|
||||
<details className="group text-xs text-muted-foreground">
|
||||
<summary className="flex cursor-pointer list-none items-center gap-2 rounded-md px-2 py-1 font-mono hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
|
||||
<span className="transition-transform group-open:rotate-90">›</span>
|
||||
<span className="transition-transform motion-reduce:transition-none group-open:rotate-90">
|
||||
›
|
||||
</span>
|
||||
<span className="font-medium text-foreground">{frame.provider}</span>
|
||||
<span className="truncate">{summary ?? nativeChatProviderFrameSummary(block)}</span>
|
||||
{frame.payload.truncated ? (
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useId } from 'react'
|
||||
import { Loader2 } from 'lucide-react'
|
||||
import type { CommentMarkdownLinkClickHandler } from '@/components/sidebar/CommentMarkdown'
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
@@ -14,6 +13,7 @@ import {
|
||||
NativeChatReasoningChevron
|
||||
} from './NativeChatReasoningDisclosure'
|
||||
import { useNativeChatDisclosure } from './native-chat-disclosure-store'
|
||||
import { LoadingSpinner } from '@/components/LoadingSpinner'
|
||||
|
||||
// Literal keys with literal fallbacks: a dynamic key registers no catalog reference.
|
||||
function statusLabel(key: Extract<NativeChatActiveTurnLabel, { source: 'status' }>['key']): string {
|
||||
@@ -63,7 +63,7 @@ export function NativeChatTurnActivityLine({
|
||||
aria-live="polite"
|
||||
aria-atomic="true"
|
||||
>
|
||||
<Loader2 aria-hidden className="size-4 shrink-0 animate-spin motion-reduce:animate-none" />
|
||||
<LoadingSpinner className="size-4 shrink-0" />
|
||||
<span id={labelId} className="min-w-0 truncate text-foreground/85">
|
||||
{label}
|
||||
</span>
|
||||
|
||||
@@ -73,7 +73,10 @@ export function NativeChatWorkingStatus({
|
||||
const caret =
|
||||
workedSeconds != null && onToggleExpanded ? (
|
||||
<ChevronRight
|
||||
className={cn('size-3.5 transition-transform', expanded && 'rotate-90')}
|
||||
className={cn(
|
||||
'size-3.5 transition-transform motion-reduce:transition-none',
|
||||
expanded && 'rotate-90'
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
) : null
|
||||
|
||||
+2
-2
File diff suppressed because one or more lines are too long
@@ -33,7 +33,7 @@ function DropdownMenuContent({
|
||||
data-slot="dropdown-menu-content"
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
'z-[70] max-h-(--radix-dropdown-menu-content-available-height) min-w-[11rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto scrollbar-sleek rounded-[11px] border border-black/14 bg-[rgba(255,255,255,0.82)] p-[4px] text-black dark:text-white shadow-[0_12px_28px_rgba(0,0,0,0.22),inset_0_1px_0_rgba(255,255,255,0.14)] backdrop-blur-2xl dark:border-white/14 dark:bg-[rgba(0,0,0,0.72)] dark:shadow-[0_16px_34px_rgba(0,0,0,0.40),inset_0_1px_0_rgba(255,255,255,0.04)] data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:duration-150 data-[state=open]:zoom-in-[0.98]',
|
||||
'z-[70] max-h-(--radix-dropdown-menu-content-available-height) min-w-[11rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto scrollbar-sleek rounded-[11px] border border-black/14 bg-[rgba(255,255,255,0.82)] p-[4px] text-black dark:text-white shadow-[0_12px_28px_rgba(0,0,0,0.22),inset_0_1px_0_rgba(255,255,255,0.14)] backdrop-blur-2xl dark:border-white/14 dark:bg-[rgba(0,0,0,0.72)] dark:shadow-[0_16px_34px_rgba(0,0,0,0.40),inset_0_1px_0_rgba(255,255,255,0.04)] data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in motion-reduce:data-[state=open]:animate-none motion-reduce:data-[state=closed]:animate-none data-[state=open]:fade-in-0 data-[state=open]:duration-150 data-[state=open]:zoom-in-[0.98]',
|
||||
className
|
||||
)}
|
||||
// Why: Electron's -webkit-app-region: drag on the titlebar captures
|
||||
@@ -213,7 +213,7 @@ function DropdownMenuSubContent({
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
data-slot="dropdown-menu-sub-content"
|
||||
className={cn(
|
||||
'z-[70] min-w-[11rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-[11px] border border-black/14 bg-[rgba(255,255,255,0.82)] p-[4px] text-black dark:text-white shadow-[0_12px_28px_rgba(0,0,0,0.22),inset_0_1px_0_rgba(255,255,255,0.14)] backdrop-blur-2xl dark:border-white/14 dark:bg-[rgba(0,0,0,0.72)] dark:shadow-[0_16px_34px_rgba(0,0,0,0.40),inset_0_1px_0_rgba(255,255,255,0.04)] data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:duration-150 data-[state=open]:zoom-in-[0.98]',
|
||||
'z-[70] min-w-[11rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-[11px] border border-black/14 bg-[rgba(255,255,255,0.82)] p-[4px] text-black dark:text-white shadow-[0_12px_28px_rgba(0,0,0,0.22),inset_0_1px_0_rgba(255,255,255,0.14)] backdrop-blur-2xl dark:border-white/14 dark:bg-[rgba(0,0,0,0.72)] dark:shadow-[0_16px_34px_rgba(0,0,0,0.40),inset_0_1px_0_rgba(255,255,255,0.04)] data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in motion-reduce:data-[state=open]:animate-none motion-reduce:data-[state=closed]:animate-none data-[state=open]:fade-in-0 data-[state=open]:duration-150 data-[state=open]:zoom-in-[0.98]',
|
||||
className
|
||||
)}
|
||||
// Why: same no-drag fix as DropdownMenuContent — titlebar drag region
|
||||
|
||||
@@ -45,7 +45,7 @@ function HoverCardContent({
|
||||
// dark canvas (#171717 vs #0a0a0a, ~3% white lift) with a near-
|
||||
// invisible border.
|
||||
className={cn(
|
||||
'z-50 origin-(--radix-hover-card-content-transform-origin) rounded-md border border-black/14 bg-[rgba(255,255,255,0.82)] text-popover-foreground shadow-[0_16px_36px_rgba(0,0,0,0.24),inset_0_1px_0_rgba(255,255,255,0.14)] backdrop-blur-2xl outline-hidden dark:border-white/14 dark:bg-[rgba(0,0,0,0.72)] dark:shadow-[0_20px_44px_rgba(0,0,0,0.42),inset_0_1px_0_rgba(255,255,255,0.04)] data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
||||
'z-50 origin-(--radix-hover-card-content-transform-origin) rounded-md border border-black/14 bg-[rgba(255,255,255,0.82)] text-popover-foreground shadow-[0_16px_36px_rgba(0,0,0,0.24),inset_0_1px_0_rgba(255,255,255,0.14)] backdrop-blur-2xl outline-hidden dark:border-white/14 dark:bg-[rgba(0,0,0,0.72)] dark:shadow-[0_20px_44px_rgba(0,0,0,0.42),inset_0_1px_0_rgba(255,255,255,0.04)] data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in motion-reduce:data-[state=open]:animate-none motion-reduce:data-[state=closed]:animate-none data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
||||
hoverCardContentVariants({ variant }),
|
||||
className
|
||||
)}
|
||||
|
||||
@@ -171,7 +171,7 @@ function PopoverContent({
|
||||
// the canvas in dark mode (#171717 vs #0a0a0a) and border-border/50
|
||||
// is too faint to read, so the popover blended into the background.
|
||||
className={cn(
|
||||
'z-[60] overflow-hidden rounded-md border border-black/14 bg-[rgba(255,255,255,0.82)] text-popover-foreground shadow-[0_16px_36px_rgba(0,0,0,0.24),inset_0_1px_0_rgba(255,255,255,0.14)] backdrop-blur-2xl outline-none dark:border-white/14 dark:bg-[rgba(0,0,0,0.72)] dark:shadow-[0_20px_44px_rgba(0,0,0,0.42),inset_0_1px_0_rgba(255,255,255,0.04)] data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
||||
'z-[60] overflow-hidden rounded-md border border-black/14 bg-[rgba(255,255,255,0.82)] text-popover-foreground shadow-[0_16px_36px_rgba(0,0,0,0.24),inset_0_1px_0_rgba(255,255,255,0.14)] backdrop-blur-2xl outline-none dark:border-white/14 dark:bg-[rgba(0,0,0,0.72)] dark:shadow-[0_20px_44px_rgba(0,0,0,0.42),inset_0_1px_0_rgba(255,255,255,0.04)] data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in motion-reduce:data-[state=open]:animate-none motion-reduce:data-[state=closed]:animate-none data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
||||
wheelScroll && 'popover-wheel-scroll',
|
||||
className
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user