Files
Danny White babebc959c fix(studio): improve pipeline destination logo legibility (#50496)
## What kind of change does this PR introduce?

UI polish for Pipeline destination logos.

## What is the current behavior?

The Snowflake mark does not use the available SVG canvas, and
destination marks appear overly inset in the pipeline detail header.

## What is the new behavior?

The Snowflake asset uses more of its canvas, and the large
`DestinationLogo` variant renders a 32px mark inside its existing 56px
frame. Small logos used in lists, diagrams, and destination pickers
remain unchanged.

| Before | After |
| --- | --- |
| <img width="780" height="160" alt="CleanShot 2026-09-17 at 12 46
51@2x"
src="https://github.com/user-attachments/assets/83e7ab5e-c9f3-4410-99c1-4f3596b9e027"
/> | <img width="780" height="160" alt="CleanShot 2026-09-17 at 12 48
33@2x"
src="https://github.com/user-attachments/assets/d354dd46-80be-48f6-b2d9-277ee930eaaa"
/> |

## To test

1. Open `/project/<ref>/database/replication` with a Snowflake pipeline
and confirm its logo renders clearly in the list.
2. Open that pipeline's child route and confirm the destination logo
fills more of the header square without changing the square itself.
3. Check another destination's child route and confirm its large logo
uses the same sizing.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Style**
* Increased the size of the large destination logo mark for improved
visibility.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-17 11:07:31 +08:00

62 lines
2.1 KiB
TypeScript

import { cn, StatusIcon } from 'ui'
import { DestinationIcon } from './DestinationIcon'
import type { DestinationType } from './DestinationPanel/DestinationPanel.types'
import { BASE_PATH } from '@/lib/constants'
// Destinations with a brand mark. Anything absent falls back to the line icon in the same frame,
// so a new destination type never renders an empty square.
const BRAND_MARK_BY_TYPE: Partial<Record<DestinationType, string>> = {
BigQuery: `${BASE_PATH}/img/icons/bigquery-icon.svg`,
ClickHouse: `${BASE_PATH}/img/icons/clickhouse-icon.svg`,
DuckLake: `${BASE_PATH}/img/icons/ducklake-icon.svg`,
Snowflake: `${BASE_PATH}/img/icons/snowflake-icon.svg`,
}
const SIZE_CLASS_NAME = {
small: { frame: 'h-8 w-8 rounded-md', mark: 'h-4 w-4', icon: 16 },
large: { frame: 'h-14 w-14 rounded-lg', mark: 'h-8 w-8', icon: 32 },
} as const
interface DestinationLogoProps {
type: DestinationType
size?: keyof typeof SIZE_CLASS_NAME
className?: string
/** Destructive badge on the bottom-right corner (e.g. table replication errors). */
hasErrors?: boolean
}
/**
* A destination's brand mark in a square app-icon frame, used wherever a destination is the
* subject: the list rows, the pipeline header, and the replication diagram.
*/
export const DestinationLogo = ({
type,
size = 'small',
className,
hasErrors = false,
}: DestinationLogoProps) => {
const sizing = SIZE_CLASS_NAME[size]
const brandMark = BRAND_MARK_BY_TYPE[type]
return (
<span className={cn('relative inline-flex shrink-0', className)}>
<span className={cn('flex items-center justify-center border bg-surface-100', sizing.frame)}>
{brandMark === undefined ? (
<DestinationIcon type={type} size={sizing.icon} className="text-foreground-light" />
) : (
<img src={brandMark} alt="" aria-hidden className={sizing.mark} />
)}
</span>
{hasErrors && (
<span
className="absolute -bottom-1 -right-1 rounded-sm bg-background outline outline-2 outline-background"
aria-hidden
>
<StatusIcon variant="destructive" />
</span>
)}
</span>
)
}