Skip to content

fix(web): prevent list cell text and badge overflow - #5510

Merged
t0ng7u merged 2 commits into
mainfrom
fix/table-cell-text-overflow
Jun 15, 2026
Merged

fix(web): prevent list cell text and badge overflow#5510
t0ng7u merged 2 commits into
mainfrom
fix/table-cell-text-overflow

Conversation

@QuentinHsu

@QuentinHsu QuentinHsu commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

⚠️ 提交说明 / PR Notice

Important

  • 请提供人工撰写的简洁摘要,避免直接粘贴未经整理的 AI 输出。
  • 本 PR 的代码改动包含 AI 辅助生成内容,提交前已进行人工审查与本地验证。

📝 变更描述 / Description

问题描述

  • 部分列表页中长文本、分组徽标、模型/IP 限制、URL、provider 等内容在列宽不足时可能溢出到相邻列。
  • /keys 页面的分组、模型、IP 限制等徽标内容也存在与列头左侧对齐不一致的问题。

修复方式

  • 在 DataTableRow 和 StaticDataTable 层为普通文本单元格统一增加截断与 tooltip 展示完整内容。
  • 新增 BadgeCell,用于统一徽标类单元格的 min-w-0、overflow 裁剪和左侧对齐。
  • 调整 StatusBadge、GroupBadge、BadgeListCell、TruncatedText 等基础展示组件,使其在受限列宽内可收缩并保持完整内容可查看。
  • 覆盖 API keys、models、pricing、subscriptions、users、redemption codes、deployments、自定义 OAuth、API info 和 upstream ratio sync 等列表风险点。

测试说明

  • 已运行 bunx eslint 检查相关变更文件。
  • 已运行 git diff --check
  • 已运行 bun run typecheck
  • 已运行 bun run build

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix) - 请关联对应 Issue,避免将设计取舍、理解偏差或预期不一致直接归类为 bug
  • ✨ 新功能 (New feature) - 重大特性建议先通过 Issue 沟通
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

  • 无(如有)

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 我已搜索现有的 Issues 与 PRs,确认不是重复提交。
  • Bug fix 说明: 若此 PR 标记为 Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。
  • 变更理解: 我已理解这些更改的工作原理及可能影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 本地验证: 已在本地运行并通过测试或手动验证,维护者可以据此复核结果。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

📸 运行证明 / Proof of Work

  • bunx eslint ...
  • git diff --check
  • bun run typecheck
  • bun run build
CleanShot 2026-06-15 at 14 10 09

Summary by CodeRabbit

  • New Features

    • Tables now automatically truncate long text in cells with hover tooltips for full content visibility.
    • New reusable badge cell component for consistent badge styling across all tables.
  • Improvements

    • Enhanced table layouts with better overflow handling and content constraint management across API keys, models, pricing, subscriptions, users, and system settings tables.

- add default truncation with hover details for text cells in shared and static data tables to prevent content from spilling into adjacent columns.
- adjust API key group, model, and IP restriction columns to fix badge overlap and left alignment drift.
- reuse a shared truncated cell component and add width constraints for composite badge cells.
- make table text and badge cells shrink within constrained columns so long values truncate instead of bleeding into adjacent cells.
- add a shared BadgeCell wrapper to keep badge alignment consistent across API keys and other list pages.
- update affected list views to use constrained wrappers for group, provider, pricing, OAuth, and API info values.
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Introduces two new shared data-table cell components — BadgeCell (flex container with data-slot status-badge selectors) and TruncatedCell (tooltip-backed truncated display with recursive text extraction) — exported from the data-table index. DataTableRow and StaticDataTable are wired to automatically wrap primitive cell content in TruncatedCell. StatusBadge and GroupBadge gain min-w-0 / shrink constraints. TruncatedText is simplified to use TruncatedCell directly. The new components are then adopted across API keys, models, pricing, subscriptions, users, redemptions, deployments, upstream-ratio, and OAuth provider column definitions. ApiInfoSection also replaces useEffect-based state with a draft-buffer pattern.

Changes

BadgeCell & TruncatedCell: new cell components + data-table truncation wiring

Layer / File(s) Summary
BadgeCell and TruncatedCell component definitions
web/default/src/components/data-table/core/badge-cell.tsx, web/default/src/components/data-table/core/truncated-cell.tsx, web/default/src/components/data-table/index.ts
BadgeCell renders a flex div with data-slot status-badge selectors and cn-merged class. TruncatedCell recursively extracts text from children to auto-derive tooltip content and falls back to a plain truncated div when no text is found. Both are re-exported from the data-table index.
StatusBadge and GroupBadge layout constraint updates
web/default/src/components/status-badge.tsx, web/default/src/components/group-badge.tsx
StatusBadge gains min-w-0, drops shrink-0 for shrink, and derives title from copyable/label. StatusBadgeList gains min-w-0. GroupBadge passes className through cn with overflow constraints and wraps the ratio path in max-w-full min-w-0 overflow-hidden spans.
DataTableRow, StaticDataTable, TruncatedText, BadgeListCell auto-truncation
web/default/src/components/data-table/core/data-table-row.tsx, web/default/src/components/data-table/static/static-data-table.tsx, web/default/src/components/truncated-text.tsx, web/default/src/components/data-table/core/badge-list-cell.tsx
DataTableRow always applies overflow sizing to TableCell and routes output through renderCellContent/getPrimitiveTextContent, wrapping primitive strings/numbers in TruncatedCell. StaticDataTable gains the same renderStaticCellContent path. TruncatedText is simplified to a direct TruncatedCell render. BadgeListCell trigger wrapper gains max-w-full.
Feature column BadgeCell / TruncatedCell / layout adoption
web/default/src/features/keys/components/api-keys-cells.tsx, web/default/src/features/keys/components/api-keys-columns.tsx, web/default/src/features/models/components/models-columns.tsx, web/default/src/features/models/components/deployments-columns.tsx, web/default/src/features/pricing/components/pricing-columns.tsx, web/default/src/features/subscriptions/components/subscriptions-columns.tsx, web/default/src/features/users/components/users-columns.tsx, web/default/src/features/redemption-codes/components/redemptions-columns.tsx, web/default/src/features/system-settings/auth/custom-oauth/components/provider-table.tsx, web/default/src/features/system-settings/models/upstream-ratio-sync-columns.tsx
BadgeCell replaces plain span/div tooltip trigger wrappers for badge cells across all affected feature columns. TruncatedCell wraps non-auto group names in API keys. Fixed min-w-[...] constraints are replaced with max-w-full min-w-0 throughout.
ApiInfoSection draft-buffer state refactor + BadgeCell adoption
web/default/src/features/system-settings/content/api-info-section.tsx
Replaces useEffect-based JSON parsing and direct state mutation with a parseApiInfoList() helper and draftApiInfoList/isEnabledDraft draft buffers. Delete, submit, and enable-toggle handlers write into the draft; saving clears it. URL and route column cells are wrapped in BadgeCell.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • QuantumNous/new-api#5460: Modifies data-table-row.tsx and introduces BadgeListCell cell rendering patterns that this PR extends with BadgeCell and TruncatedCell.
  • QuantumNous/new-api#5405: Overhauls the same DataTableRow/table system that this PR further modifies with auto-truncation and class composition.

Poem

🐇 A rabbit hops through table cells,
No more fixed widths, no overflows!
BadgeCell wraps each badge so well,
TruncatedCell trims the text that grows.
Drafts buffer state, then save with flair —
Min-w-zero fills every square! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main purpose of the changeset, which focuses on preventing text and badge overflow in list cells across multiple table components.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/table-cell-text-overflow

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@QuentinHsu QuentinHsu changed the title Fix/table cell text overflow fix(web): prevent list cell text and badge overflow Jun 15, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/default/src/features/subscriptions/components/subscriptions-columns.tsx (1)

139-152: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize payment channel badge labels

Line 141, Line 147, and Line 151 use hardcoded user-facing text (Stripe, Creem, Waffo Pancake) instead of t(...), so these labels won’t be translated.

Suggested fix
               {plan.stripe_price_id && (
                 <StatusBadge
-                  label='Stripe'
+                  label={t('Stripe')}
                   variant='neutral'
                   copyable={false}
                 />
               )}
               {plan.creem_product_id && (
-                <StatusBadge label='Creem' variant='neutral' copyable={false} />
+                <StatusBadge
+                  label={t('Creem')}
+                  variant='neutral'
+                  copyable={false}
+                />
               )}
               {plan.waffo_pancake_product_id && (
                 <StatusBadge
-                  label='Waffo Pancake'
+                  label={t('Waffo Pancake')}
                   variant='neutral'
                   copyable={false}
                 />
               )}

As per coding guidelines, "All user-facing text content must support i18n using the t() function from useTranslation() in React components".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/subscriptions/components/subscriptions-columns.tsx`
around lines 139 - 152, Replace the hardcoded user-facing text labels in the
StatusBadge components with internationalized strings using the t() function.
For the three StatusBadge components in subscriptions-columns.tsx with label
props containing 'Stripe', 'Creem', and 'Waffo Pancake', wrap each label string
with the t() function to ensure proper translation support. Make sure you are
using the t function from the useTranslation() hook that should already be
available in this React component.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/default/src/components/data-table/core/truncated-cell.tsx`:
- Around line 87-90: The getTextContent function currently handles strings,
numbers, and arrays but returns an empty string for React elements, causing
tooltips to be skipped for components like span or other React elements. Extend
the function to check if the node is a React element (using React.isValidElement
or similar check), and if so, recursively extract text from its children
property or children prop to preserve the text content from nested React
elements rather than silently dropping it with an empty return.

In `@web/default/src/components/status-badge.tsx`:
- Around line 158-160: The hardcoded English string "Click to copy: " in the
title variable assignment bypasses i18n and violates localization guidelines.
Extract the "Click to copy: " text and wrap it with the t() function from
useTranslation() to properly localize this user-facing message. Ensure the
component is using the useTranslation hook and replace the hardcoded string with
a translation key that includes both the instructional text and the dynamic
content (copyText or label).

In `@web/default/src/features/system-settings/content/api-info-section.tsx`:
- Around line 148-156: The handleToggleEnabled function updates the UI state and
shows a success message even when the API mutation resolves with success: false.
Capture the response from updateOption.mutateAsync and verify that the response
contains success: true before proceeding to call setIsEnabledDraft(checked) and
toast.success(). If success is false, handle that case appropriately (such as
showing an error message or reverting the draft state).

---

Outside diff comments:
In `@web/default/src/features/subscriptions/components/subscriptions-columns.tsx`:
- Around line 139-152: Replace the hardcoded user-facing text labels in the
StatusBadge components with internationalized strings using the t() function.
For the three StatusBadge components in subscriptions-columns.tsx with label
props containing 'Stripe', 'Creem', and 'Waffo Pancake', wrap each label string
with the t() function to ensure proper translation support. Make sure you are
using the t function from the useTranslation() hook that should already be
available in this React component.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f3b8c0ae-66b4-4996-a250-b42cc9c9e8cf

📥 Commits

Reviewing files that changed from the base of the PR and between 1ac0f58 and c586ebd.

📒 Files selected for processing (20)
  • web/default/src/components/data-table/core/badge-cell.tsx
  • web/default/src/components/data-table/core/badge-list-cell.tsx
  • web/default/src/components/data-table/core/data-table-row.tsx
  • web/default/src/components/data-table/core/truncated-cell.tsx
  • web/default/src/components/data-table/index.ts
  • web/default/src/components/data-table/static/static-data-table.tsx
  • web/default/src/components/group-badge.tsx
  • web/default/src/components/status-badge.tsx
  • web/default/src/components/truncated-text.tsx
  • web/default/src/features/keys/components/api-keys-cells.tsx
  • web/default/src/features/keys/components/api-keys-columns.tsx
  • web/default/src/features/models/components/deployments-columns.tsx
  • web/default/src/features/models/components/models-columns.tsx
  • web/default/src/features/pricing/components/pricing-columns.tsx
  • web/default/src/features/redemption-codes/components/redemptions-columns.tsx
  • web/default/src/features/subscriptions/components/subscriptions-columns.tsx
  • web/default/src/features/system-settings/auth/custom-oauth/components/provider-table.tsx
  • web/default/src/features/system-settings/content/api-info-section.tsx
  • web/default/src/features/system-settings/models/upstream-ratio-sync-columns.tsx
  • web/default/src/features/users/components/users-columns.tsx

Comment on lines +87 to +90
function getTextContent(node: React.ReactNode): string {
if (typeof node === 'string' || typeof node === 'number') return String(node)
if (Array.isArray(node)) return node.map(getTextContent).join('')
return ''

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle React element children in text extraction.

On Line 87, getTextContent skips valid React elements, so <TruncatedCell><span>foo</span></TruncatedCell> won’t derive tooltip text and silently drops the tooltip fallback path.

Suggested fix
 function getTextContent(node: React.ReactNode): string {
   if (typeof node === 'string' || typeof node === 'number') return String(node)
   if (Array.isArray(node)) return node.map(getTextContent).join('')
+  if (React.isValidElement(node)) return getTextContent(node.props.children)
   return ''
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/components/data-table/core/truncated-cell.tsx` around lines
87 - 90, The getTextContent function currently handles strings, numbers, and
arrays but returns an empty string for React elements, causing tooltips to be
skipped for components like span or other React elements. Extend the function to
check if the node is a React element (using React.isValidElement or similar
check), and if so, recursively extract text from its children property or
children prop to preserve the text content from nested React elements rather
than silently dropping it with an empty return.

Comment on lines +158 to +160
const title = copyable
? `Click to copy: ${copyText || label || ''}`
: label || undefined

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize the new title text.

On Line 159, "Click to copy: ..." is hardcoded English and bypasses i18n.

Suggested fix
 import * as React from 'react'
+import { t } from 'i18next'
 ...
   const title = copyable
-    ? `Click to copy: ${copyText || label || ''}`
+    ? t('Click to copy: {{value}}', { value: copyText || label || '' })
     : label || undefined

As per coding guidelines, “All user-facing text content must support i18n using the t() function from useTranslation() in React components.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/components/status-badge.tsx` around lines 158 - 160, The
hardcoded English string "Click to copy: " in the title variable assignment
bypasses i18n and violates localization guidelines. Extract the "Click to copy:
" text and wrap it with the t() function from useTranslation() to properly
localize this user-facing message. Ensure the component is using the
useTranslation hook and replace the hardcoded string with a translation key that
includes both the instructional text and the dynamic content (copyText or
label).

Source: Coding guidelines

Comment on lines 148 to 156
const handleToggleEnabled = async (checked: boolean) => {
try {
await updateOption.mutateAsync({
key: 'console_setting.api_info_enabled',
value: checked,
})
setIsEnabled(checked)
setIsEnabledDraft(checked)
toast.success(t('Setting saved'))
} catch {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Gate enabled-state update on mutation result success

Line 154 and Line 155 run even when mutateAsync resolves with success: false, which can desync UI state and show a false success message.

Suggested fix
   const handleToggleEnabled = async (checked: boolean) => {
     try {
-      await updateOption.mutateAsync({
+      const result = await updateOption.mutateAsync({
         key: 'console_setting.api_info_enabled',
         value: checked,
       })
-      setIsEnabledDraft(checked)
-      toast.success(t('Setting saved'))
+      if (!result.success) return
+      setIsEnabledDraft(checked)
+      // Optional: remove local success toast if useUpdateOption already toasts on success
     } catch {
       toast.error(t('Failed to update setting'))
     }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleToggleEnabled = async (checked: boolean) => {
try {
await updateOption.mutateAsync({
key: 'console_setting.api_info_enabled',
value: checked,
})
setIsEnabled(checked)
setIsEnabledDraft(checked)
toast.success(t('Setting saved'))
} catch {
const handleToggleEnabled = async (checked: boolean) => {
try {
const result = await updateOption.mutateAsync({
key: 'console_setting.api_info_enabled',
value: checked,
})
if (!result.success) return
setIsEnabledDraft(checked)
toast.success(t('Setting saved'))
} catch {
toast.error(t('Failed to update setting'))
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/system-settings/content/api-info-section.tsx` around
lines 148 - 156, The handleToggleEnabled function updates the UI state and shows
a success message even when the API mutation resolves with success: false.
Capture the response from updateOption.mutateAsync and verify that the response
contains success: true before proceeding to call setIsEnabledDraft(checked) and
toast.success(). If success is false, handle that case appropriately (such as
showing an error message or reverting the draft state).

@t0ng7u t0ng7u self-assigned this Jun 15, 2026
@t0ng7u
t0ng7u merged commit 3c1bb0a into main Jun 15, 2026
2 checks passed
YeMao11 pushed a commit to YeMao11/new-api that referenced this pull request Jun 16, 2026
Upstream changes (50+ commits, v1.0.0-rc.11):
- data-table perf: row selection memo, column pinning, badge display
- Fixes: channel test dialog (QuantumNous#5517), CC Switch model selector (QuantumNous#5515),
  API key form options (QuantumNous#5512), cell overflow (QuantumNous#5510), kimi k2.6 temp (QuantumNous#5390),
  Anthropic-compatible GLM chunked encoding (QuantumNous#5307), streaming image relay (QuantumNous#4608)
- Feat: audit auth method tracking (QuantumNous#5462), channel affinity clear toggle (QuantumNous#5306),
  relay idle timeout config (QuantumNous#5309), 6-decimal pricing precision (QuantumNous#5332)
- Classic frontend: Rsbuild support, Semi React 19 adapter
- Shared dialog wrapper, JSON code editor, debounce channel search

Conflict resolved: web/bun.lock (accepted upstream, will regenerate)

Co-Authored-By: Claude <noreply@anthropic.com>
@Calcium-Ion
Calcium-Ion deleted the fix/table-cell-text-overflow branch June 24, 2026 10:42
ruanhangjian pushed a commit to ruanhangjian/new-api that referenced this pull request Jul 11, 2026
* fix(ui): prevent table cell text overflow

- add default truncation with hover details for text cells in shared and static data tables to prevent content from spilling into adjacent columns.
- adjust API key group, model, and IP restriction columns to fix badge overlap and left alignment drift.
- reuse a shared truncated cell component and add width constraints for composite badge cells.

* fix(table): prevent badge content from overflowing columns

- make table text and badge cells shrink within constrained columns so long values truncate instead of bleeding into adjacent cells.
- add a shared BadgeCell wrapper to keep badge alignment consistent across API keys and other list pages.
- update affected list views to use constrained wrappers for group, provider, pricing, OAuth, and API info values.
zhaodechao2008 pushed a commit to zhaodechao2008/new-api that referenced this pull request Jul 27, 2026
* fix(ui): prevent table cell text overflow

- add default truncation with hover details for text cells in shared and static data tables to prevent content from spilling into adjacent columns.
- adjust API key group, model, and IP restriction columns to fix badge overlap and left alignment drift.
- reuse a shared truncated cell component and add width constraints for composite badge cells.

* fix(table): prevent badge content from overflowing columns

- make table text and badge cells shrink within constrained columns so long values truncate instead of bleeding into adjacent cells.
- add a shared BadgeCell wrapper to keep badge alignment consistent across API keys and other list pages.
- update affected list views to use constrained wrappers for group, provider, pricing, OAuth, and API info values.
330079598 pushed a commit to 330079598/new-api that referenced this pull request Aug 19, 2026
* fix(ui): prevent table cell text overflow

- add default truncation with hover details for text cells in shared and static data tables to prevent content from spilling into adjacent columns.
- adjust API key group, model, and IP restriction columns to fix badge overlap and left alignment drift.
- reuse a shared truncated cell component and add width constraints for composite badge cells.

* fix(table): prevent badge content from overflowing columns

- make table text and badge cells shrink within constrained columns so long values truncate instead of bleeding into adjacent cells.
- add a shared BadgeCell wrapper to keep badge alignment consistent across API keys and other list pages.
- update affected list views to use constrained wrappers for group, provider, pricing, OAuth, and API info values.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants