Skip to content

Commit 5b5c042

Browse files
authored
feat: add masked key to api keys table (#345)
* Add masked key to API keys table * Tighten up spacing for columns | Before | After | | ------- | ------- | | <img width="2330" height="742" alt="CleanShot 2026-05-27 at 13 21 49@2x" src="https://github.com/user-attachments/assets/e6bff72d-d021-4fe8-a7a1-89c84300b2ac" /> | <img width="1728" height="728" alt="CleanShot 2026-05-27 at 13 41 03@2x" src="https://github.com/user-attachments/assets/4318f07c-24ef-4a61-990d-3c5506cf76ff" /> |
1 parent 6b10529 commit 5b5c042

4 files changed

Lines changed: 31 additions & 14 deletions

File tree

src/features/dashboard/settings/keys/api-keys-table-row.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
TooltipContent,
1717
TooltipTrigger,
1818
} from '@/ui/primitives/tooltip'
19-
import { getLastUsedLabel } from './api-keys-utils'
19+
import { formatMaskedApiKey, getLastUsedLabel } from './api-keys-utils'
2020

2121
const tableCellClassName = 'py-3 text-left [tr:first-child>&]:pt-1.5'
2222

@@ -33,6 +33,7 @@ export const ApiKeysTableRow = ({ apiKey, onDelete }: ApiKeysTableRowProps) => {
3333
? (formatDate(new Date(apiKey.createdAt), 'MMM d, yyyy') ?? '—')
3434
: '—'
3535

36+
const maskedKey = formatMaskedApiKey(apiKey)
3637
const lastUsedAt = apiKey.lastUsed
3738
const lastUsedLabel = getLastUsedLabel(apiKey)
3839
const isCliKey = apiKey.name === CLI_GENERATED_KEY_NAME
@@ -58,6 +59,11 @@ export const ApiKeysTableRow = ({ apiKey, onDelete }: ApiKeysTableRowProps) => {
5859
</span>
5960
</div>
6061
</TableCell>
62+
<TableCell className={tableCellClassName}>
63+
<span className="text-fg-tertiary truncate font-mono text-sm tabular-nums">
64+
{maskedKey}
65+
</span>
66+
</TableCell>
6167
<TableCell className={tableCellClassName}>
6268
<IdBadge
6369
id={apiKey.id}
@@ -79,14 +85,12 @@ export const ApiKeysTableRow = ({ apiKey, onDelete }: ApiKeysTableRowProps) => {
7985
lastUsedLabel
8086
)}
8187
</TableCell>
82-
<TableCell
83-
className={cn(tableCellClassName, 'pl-3 pr-0 text-sm text-fg-tertiary')}
84-
>
85-
<div className="flex items-center gap-6 justify-between">
88+
<TableCell className={cn(tableCellClassName, 'text-sm text-fg-tertiary')}>
89+
<div className="flex items-center gap-3">
8690
<span className="block w-[92px] shrink-0 whitespace-nowrap">
8791
{addedDate}
8892
</span>
89-
<div className="flex items-center gap-6">
93+
<div className="flex items-center gap-3">
9094
{isCliKey ? (
9195
<Tooltip>
9296
<TooltipTrigger asChild>

src/features/dashboard/settings/keys/api-keys-table.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,16 @@ export const ApiKeysTable: FC<ApiKeysTableProps> = ({
4646
/>
4747
<Table className={cn('w-full table-fixed', className)}>
4848
<colgroup>
49-
<col className="min-w-[260px] lg:w-[48%]" />
50-
<col className="w-[132px] lg:w-[16%]" />
51-
<col className="w-[96px] lg:w-[12%]" />
52-
<col className="w-[180px] lg:w-[24%]" />
49+
<col className="min-w-[220px]" />
50+
<col className="w-[150px]" />
51+
<col className="w-[135px]" />
52+
<col className="w-[106px]" />
53+
<col className="w-[168px]" />
5354
</colgroup>
5455
<TableHeader className="border-b-0">
5556
<TableRow className="hover:bg-transparent">
5657
<ApiKeysTableHead>LABEL</ApiKeysTableHead>
58+
<ApiKeysTableHead>KEY</ApiKeysTableHead>
5759
<ApiKeysTableHead>ID</ApiKeysTableHead>
5860
<ApiKeysTableHead>LAST USED</ApiKeysTableHead>
5961
<ApiKeysTableHead>ADDED</ApiKeysTableHead>
@@ -68,9 +70,9 @@ export const ApiKeysTable: FC<ApiKeysTableProps> = ({
6870
)}
6971
>
7072
{isLoading ? (
71-
<TableLoadingState colSpan={4} label="Loading API keys" />
73+
<TableLoadingState colSpan={5} label="Loading API keys" />
7274
) : apiKeys.length === 0 ? (
73-
<TableEmptyState colSpan={4}>
75+
<TableEmptyState colSpan={5}>
7476
<KeyIcon
7577
aria-hidden
7678
className={cn(

src/features/dashboard/settings/keys/api-keys-utils.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ const getMaskedIdSearchString = (apiKey: TeamAPIKey): string => {
88
return `${prefix}${maskedValuePrefix}...${maskedValueSuffix}`.toLowerCase()
99
}
1010

11+
/** Visual masked key for the KEY column; e.g. `"e2b_1f••••6ba3"`. */
12+
const formatMaskedApiKey = (apiKey: TeamAPIKey): string => {
13+
const { prefix, maskedValuePrefix, maskedValueSuffix } = apiKey.mask
14+
return `${prefix}${maskedValuePrefix}••••${maskedValueSuffix}`.toLowerCase()
15+
}
16+
1117
/** Returns true when the key name or masked id contains the trimmed query (case-insensitive). */
1218
const matchesApiKeySearch = (apiKey: TeamAPIKey, query: string): boolean => {
1319
const q = query.trim().toLowerCase()
@@ -29,4 +35,9 @@ const getLastUsedLabel = (apiKey: TeamAPIKey): string => {
2935
return 'Never'
3036
}
3137

32-
export { getLastUsedLabel, getMaskedIdSearchString, matchesApiKeySearch }
38+
export {
39+
formatMaskedApiKey,
40+
getLastUsedLabel,
41+
getMaskedIdSearchString,
42+
matchesApiKeySearch,
43+
}

src/features/dashboard/shared/id-badge.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export const IdBadge = ({
3636

3737
return (
3838
<Badge className="bg-bg-highlight text-fg-tertiary h-[18px] gap-[3px] px-1 align-middle prose-label-numeric">
39-
<span className="tracking-wider">{displayId}</span>
39+
<span className="tracking-wider font-mono">{displayId}</span>
4040
<Button
4141
type="button"
4242
variant="quaternary"

0 commit comments

Comments
 (0)