Skip to content

feat(channel): 使用表单配置获取上游模型 - #5947

Closed
zuiho-kai wants to merge 2 commits into
QuantumNous:mainfrom
zuiho-kai:codex/channel-ux-improvements
Closed

feat(channel): 使用表单配置获取上游模型#5947
zuiho-kai wants to merge 2 commits into
QuantumNous:mainfrom
zuiho-kai:codex/channel-ux-improvements

Conversation

@zuiho-kai

@zuiho-kai zuiho-kai commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

⚠️ 提交说明 / PR Notice

Important

📝 变更描述 / Description

当前在创建或编辑渠道时,获取上游模型的行为没有完全使用表单里的未保存配置:创建模式只提交 type/key/base_url,后端单独手写了一套模型列表请求逻辑;编辑模式在未保存新 key 时仍使用已保存渠道配置。这会导致表单里刚填的代理设置、header override 等配置无法参与获取模型,也容易和已保存渠道的正式获取逻辑不一致。

本 PR 将 POST /api/channel/fetch_models 改为接收渠道表单配置并复用现有 fetchChannelUpstreamModelIDs。这样 provider 特殊路径、代理、header override、Ollama/Gemini 等逻辑都和已保存渠道获取上游模型保持一致。

前端新增从渠道表单生成获取模型 payload 的转换函数。创建渠道时会直接用当前表单中的 key、Base URL、代理设置和 header override 拉取模型;编辑已有渠道时,只有用户在表单中填入新 key 才会走表单配置请求,未填写新 key 时仍保持原有按已保存渠道获取的行为,避免把已保存密钥发送到任意未保存的 Base URL。

🚀 变更类型 / Type of change

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

🔗 关联任务 / Related Issue

✅ 提交前检查项 / Checklist

📸 运行证明 / Proof of Work

已执行:

bun run typecheck
bunx oxlint -c .oxlintrc.json src/features/channels/api.ts src/features/channels/components/drawers/channel-mutate-drawer.tsx src/features/channels/lib/channel-form.ts
git diff --check upstream/main..HEAD
bun run build

说明:当前本地 Windows 环境未安装 go / gofmt,因此 Go 编译与格式检查由 CI 继续验证。

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds column-resizing support to the shared DataTable component (persisted via localStorage) and applies it to the Channels table with layout adjustments to the Name column. Reworks the channel model-fetch payload builder on the frontend and simplifies the backend FetchModels endpoint to use a unified upstream-ID fetch helper.

Changes

Column Resizing Feature

Layer / File(s) Summary
Column sizing state, hydration, and persistence
web/default/src/components/data-table/hooks/use-data-table.ts
Adds ColumnSizingState typing, options for initial/controlled sizing and storage key, a readColumnSizing localStorage helper, and effects to hydrate/persist sizing with skip-next guards.
Column width computation and resizer handle UI
web/default/src/components/data-table/core/data-table-colgroup.tsx, web/default/src/components/data-table/core/data-table-header.tsx
getColumnWidth now takes table/columnId and branches on enableColumnResizing; header renders a draggable resizer handle gated by a new shouldRenderColumnResizer helper, with i18n label.
Channels table wiring and layout
web/default/src/features/channels/components/channels-table.tsx, web/default/src/features/channels/components/channels-columns.tsx
Enables enableColumnResizing and a storage key on the Channels table, disables resizing on the select column, and updates the Name column's size and truncation layout to flex-fit.

Estimated code review effort: 3 (Moderate) | ~25 minutes

FetchModels Payload and Backend Refactor

Layer / File(s) Summary
Frontend fetch-models payload builder
web/default/src/features/channels/lib/channel-form.ts, web/default/src/features/channels/api.ts
Adds transformFormDataToFetchModelsPayload to build a normalized payload (base_url, type, key, setting, settings, header_override); tweaks buildSettingsJSON cleanup logic; extends fetchModels request type.
Channel mutate drawer fetcher wiring
web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx
Replaces createModeFetcher with formModeFetcher using the new payload builder, and supplies customFetcher only when creating or editing with a non-empty key.
Backend FetchModels rewrite
controller/channel.go
Removes Gemini-specific import/branching; FetchModels now binds a full model.Channel, validates the key, and delegates to fetchChannelUpstreamModelIDs.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Frontend
  participant FetchModelsAPI
  participant fetchChannelUpstreamModelIDs

  Frontend->>FetchModelsAPI: POST channel JSON (base_url, type, key, setting, settings, header_override)
  FetchModelsAPI->>FetchModelsAPI: validate channel.Key non-empty
  FetchModelsAPI->>fetchChannelUpstreamModelIDs: fetchChannelUpstreamModelIDs(&channel)
  fetchChannelUpstreamModelIDs-->>FetchModelsAPI: ids
  FetchModelsAPI-->>Frontend: { ids }
Loading

Possibly related PRs

  • QuantumNous/new-api#1486: Refactors controller/channel.go model fetching through fetchChannelUpstreamModelIDs, overlapping the Gemini-specific upstream model parsing changes.
  • QuantumNous/new-api#1517: Both modify controller/channel.go's model-fetching flow for Gemini, standardizing upstream model ID/auth header behavior.
  • QuantumNous/new-api#2615: Both directly modify controller/channel.go's upstream model-list fetching logic and Gemini relay handling.

Suggested reviewers: seefs001

Poem

A rabbit tugs the table's edge, 🐰
stretching names past their cramped ledge,
sizes saved for next hop's view,
while backend fetch grows lean and true.
Hop, resize, and persist with glee —
columns wide as they should be!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The upstream model-fetch/backend and form changes are unrelated to #5946’s column-resizing request. Move the upstream model-fetch work into a separate PR or link the corresponding issue so this change set stays scoped to #5946.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the main channel-list and upstream-model fetching improvements.
Linked Issues check ✅ Passed The channel table now supports drag resizing, persistence, usable widths, and double-click reset as requested in #5946.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@zuiho-kai

Copy link
Copy Markdown
Contributor Author

按维护成本和 review 范围拆分为两个独立 PR,本合并版先关闭。拆分后的 PR 会分别覆盖渠道列表列宽调整和获取上游模型体验优化。

@zuiho-kai zuiho-kai closed this Jul 6, 2026
@zuiho-kai zuiho-kai changed the title feat(web): 改进渠道列表与上游模型获取体验 feat(channel): 使用表单配置获取上游模型 Jul 6, 2026
@zuiho-kai

Copy link
Copy Markdown
Contributor Author

已按 review 范围拆分:#5948 是渠道列表表格手动调整列宽,#5949 是使用表单配置获取上游模型。本合并版保持关闭,避免重复 review。

@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: 4

🧹 Nitpick comments (3)
web/default/src/features/channels/components/channels-columns.tsx (1)

562-563: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider disabling resize on other fixed-width utility columns too.

The select column now explicitly sets enableResizing: false, which makes sense for a fixed-width checkbox column. The actions column (pinned right, icon-only) is similarly fixed-content but isn't marked non-resizable, so it will show a resize handle once enableColumnResizing: true is set table-wide — resizing a pinned action column could produce an odd/broken layout.

🤖 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/channels/components/channels-columns.tsx` around
lines 562 - 563, The fixed-width utility columns should behave consistently:
`select` is already marked non-resizable, but `actions` in
`channels-columns.tsx` should also disable resizing so the pinned icon-only
column does not show a resize handle when table-wide resizing is enabled. Update
the `actions` column definition alongside `select` to set `enableResizing:
false`, using the existing column config in the channels columns setup.
web/default/src/components/data-table/hooks/use-data-table.ts (1)

241-251: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Column-visibility and column-sizing hydration/persistence logic is now duplicated.

The refs, resolvedInitial* memo, hydration effect, and persistence effect for columnSizing closely mirror the pre-existing columnVisibility machinery. Consider extracting a shared generic helper (e.g. usePersistedTableState<T>) to avoid drift between the two nearly-identical implementations as more persisted table state (e.g. column ordering) is added later.

🤖 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/hooks/use-data-table.ts` around lines
241 - 251, Extract the duplicated hydration/persistence flow in use-data-table
from the columnVisibility and columnSizing paths into a shared generic helper
such as usePersistedTableState<T>, reusing the same patterns for resolvedInitial
state, hydrated storage key ref, skip-next-persist ref, and the
hydration/persistence effects. Wire both existing columnVisibility and
columnSizing state through the new helper so the behavior stays identical while
reducing drift for future persisted table state like column ordering.
controller/channel.go (1)

1159-1167: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider a dedicated request DTO instead of binding directly into model.Channel.

Binding the client payload straight into the persistence model couples the public request contract to internal storage schema (e.g., any future field added to model.Channel becomes implicitly bindable here). Since this handler doesn't persist the struct, the risk is currently low, but a small request struct mirroring only type/key/base_url/setting/settings/header_override would be safer and self-documenting.

♻️ Example of a narrower request DTO
+type fetchModelsRequest struct {
+	Type           int     `json:"type"`
+	Key            string  `json:"key"`
+	BaseURL        *string `json:"base_url"`
+	Setting        *string `json:"setting"`
+	OtherSettings  string  `json:"settings"`
+	HeaderOverride *string `json:"header_override"`
+}
+
 func FetchModels(c *gin.Context) {
-	var channel model.Channel
-
-	if err := c.ShouldBindJSON(&channel); err != nil {
+	var req fetchModelsRequest
+	if err := c.ShouldBindJSON(&req); err != nil {
 		c.JSON(http.StatusBadRequest, gin.H{
 			"success": false,
 			"message": "Invalid request",
 		})
 		return
 	}
+	channel := model.Channel{
+		Type:           req.Type,
+		Key:            req.Key,
+		BaseURL:        req.BaseURL,
+		Setting:        req.Setting,
+		OtherSettings:  req.OtherSettings,
+		HeaderOverride: req.HeaderOverride,
+	}
🤖 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 `@controller/channel.go` around lines 1159 - 1167, The request binding in the
channel handler currently uses model.Channel directly, which couples the API
payload to the persistence model. Update the handler to bind into a dedicated
request DTO in the same flow instead of model.Channel, and have it include only
the fields this endpoint accepts (such as type, key, base_url, setting/settings,
and header_override). Keep the existing validation/error handling, but reference
the new DTO in the binding logic so the public contract stays narrow and
explicit.
🤖 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 `@controller/channel.go`:
- Around line 1159-1188: Populate ChannelInfo before calling
fetchChannelUpstreamModelIDs in the form-based handler so GetNextEnabledKey can
detect multi-key input correctly. After ShouldBindJSON on model.Channel,
construct or fill channel.ChannelInfo with IsMultiKey set from the request
context/params as needed, and ensure the existing key-splitting logic is used
for pasted multi-line API keys instead of treating channel.Key as a single
value.

In `@web/default/src/components/data-table/core/data-table-header.tsx`:
- Around line 58-79: The column resizer in data-table-header is mouse/touch-only
and lacks keyboard accessibility. Update the resizer element in DataTableHeader
to be focusable and operable via keyboard by adding a tab stop and an onKeyDown
handler that adjusts the column size using the table/header sizing APIs. Also
expose the current sizing state with appropriate ARIA attributes on the
separator element so keyboard-only users can resize columns.
- Line 69: The new aria-label in DataTableHeader uses the literal “Resize
column” without a localized entry, so add this key to the locale JSON files
under web/default/src/i18n/locales and wire it into the existing translation
set. Update the relevant i18n resources used by data-table-header.tsx so the
t('Resize column') lookup resolves in every locale instead of falling back to
English.

In `@web/default/src/components/data-table/hooks/use-data-table.ts`:
- Around line 294-301: The current use-data-table setup keeps columnSizing
updating on every drag tick via useDataTable with columnResizeMode set to
onChange, and the persistence effect then writes to localStorage synchronously
on every change. Update the column sizing persistence path in use-data-table so
it is debounced or deferred until resizing settles, instead of writing during
the resize hot path. Keep the change focused around the
columnSizing/localStorage effect and the column resizing handlers so drag
updates remain smooth.

---

Nitpick comments:
In `@controller/channel.go`:
- Around line 1159-1167: The request binding in the channel handler currently
uses model.Channel directly, which couples the API payload to the persistence
model. Update the handler to bind into a dedicated request DTO in the same flow
instead of model.Channel, and have it include only the fields this endpoint
accepts (such as type, key, base_url, setting/settings, and header_override).
Keep the existing validation/error handling, but reference the new DTO in the
binding logic so the public contract stays narrow and explicit.

In `@web/default/src/components/data-table/hooks/use-data-table.ts`:
- Around line 241-251: Extract the duplicated hydration/persistence flow in
use-data-table from the columnVisibility and columnSizing paths into a shared
generic helper such as usePersistedTableState<T>, reusing the same patterns for
resolvedInitial state, hydrated storage key ref, skip-next-persist ref, and the
hydration/persistence effects. Wire both existing columnVisibility and
columnSizing state through the new helper so the behavior stays identical while
reducing drift for future persisted table state like column ordering.

In `@web/default/src/features/channels/components/channels-columns.tsx`:
- Around line 562-563: The fixed-width utility columns should behave
consistently: `select` is already marked non-resizable, but `actions` in
`channels-columns.tsx` should also disable resizing so the pinned icon-only
column does not show a resize handle when table-wide resizing is enabled. Update
the `actions` column definition alongside `select` to set `enableResizing:
false`, using the existing column config in the channels columns setup.
🪄 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: d6cd74fe-838a-4de1-a1c2-b4a08db22864

📥 Commits

Reviewing files that changed from the base of the PR and between 45f0484 and b8abba6.

📒 Files selected for processing (9)
  • controller/channel.go
  • web/default/src/components/data-table/core/data-table-colgroup.tsx
  • web/default/src/components/data-table/core/data-table-header.tsx
  • web/default/src/components/data-table/hooks/use-data-table.ts
  • web/default/src/features/channels/api.ts
  • web/default/src/features/channels/components/channels-columns.tsx
  • web/default/src/features/channels/components/channels-table.tsx
  • web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx
  • web/default/src/features/channels/lib/channel-form.ts

Comment thread controller/channel.go
Comment on lines +1159 to +1188
var channel model.Channel

if err := c.ShouldBindJSON(&req); err != nil {
if err := c.ShouldBindJSON(&channel); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request",
})
return
}

baseURL := req.BaseURL
if baseURL == "" {
baseURL = constant.ChannelBaseURLs[req.Type]
}

// remove line breaks and extra spaces.
key := strings.TrimSpace(req.Key)
key = strings.Split(key, "\n")[0]

if req.Type == constant.ChannelTypeOllama {
models, err := ollama.FetchOllamaModels(baseURL, key)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": fmt.Sprintf("获取Ollama模型失败: %s", err.Error()),
})
return
}

names := make([]string, 0, len(models))
for _, modelInfo := range models {
names = append(names, modelInfo.Name)
}

c.JSON(http.StatusOK, gin.H{
"success": true,
"data": names,
})
return
}

if req.Type == constant.ChannelTypeGemini {
models, err := gemini.FetchGeminiModels(baseURL, key, "")
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": fmt.Sprintf("获取Gemini模型失败: %s", err.Error()),
})
return
}

if strings.TrimSpace(channel.Key) == "" {
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": models,
})
return
}

client := &http.Client{}
url := fmt.Sprintf("%s/v1/models", baseURL)

request, err := http.NewRequest("GET", url, nil)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": err.Error(),
"message": "Please enter API key first",
})
return
}

request.Header.Set("Authorization", "Bearer "+key)

response, err := client.Do(request)
ids, err := fetchChannelUpstreamModelIDs(&channel)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": err.Error(),
})
return
}
//check status code
if response.StatusCode != http.StatusOK {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "Failed to fetch models",
})
return
}
defer response.Body.Close()

var result struct {
Data []struct {
ID string `json:"id"`
} `json:"data"`
}

if err := json.NewDecoder(response.Body).Decode(&result); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": err.Error(),
"message": fmt.Sprintf("获取模型列表失败: %s", err.Error()),
})
return
}

var models []string
for _, model := range result.Data {
models = append(models, model.ID)
}

c.JSON(http.StatusOK, gin.H{
"success": true,
"data": models,
"data": ids,

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Locate GetNextEnabledKey and inspect its dependency on Status/ChannelInfo
ast-grep run --pattern 'func (channel *Channel) GetNextEnabledKey($$$) $$$' --lang go model
rg -n -A 40 'func \(channel \*Channel\) GetNextEnabledKey' model

Repository: QuantumNous/new-api

Length of output: 7220


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect Channel shape, the fetch-models controller path, and any normalization before GetNextEnabledKey.
ast-grep outline model/channel.go --view expanded
printf '\n--- controller path ---\n'
ast-grep outline controller/channel_upstream_update.go --view expanded
printf '\n--- fetch payload source ---\n'
rg -n -A 40 -B 20 'transformFormDataToFetchModelsPayload|fetchChannelUpstreamModelIDs|GetNextEnabledKey\(' controller model

Repository: QuantumNous/new-api

Length of output: 35307


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the Channel struct, JSON tags, and the fetch-models request transformation.
sed -n '1,260p' model/channel.go | cat -n
printf '\n--- fetch-models transformation ---\n'
rg -n -A 80 -B 30 'transformFormDataToFetchModelsPayload|FetchModels|fetchChannelUpstreamModelIDs' controller

Repository: QuantumNous/new-api

Length of output: 39500


Populate ChannelInfo for this form-based fetch path. GetNextEnabledKey() ignores Status, but it does require ChannelInfo.IsMultiKey; with the current raw ShouldBindJSON payload, pasted multi-line API keys stay unsplit and are treated as one key.

🤖 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 `@controller/channel.go` around lines 1159 - 1188, Populate ChannelInfo before
calling fetchChannelUpstreamModelIDs in the form-based handler so
GetNextEnabledKey can detect multi-key input correctly. After ShouldBindJSON on
model.Channel, construct or fill channel.ChannelInfo with IsMultiKey set from
the request context/params as needed, and ensure the existing key-splitting
logic is used for pasted multi-line API keys instead of treating channel.Key as
a single value.

Comment on lines +58 to +79
className={cn(
'relative',
getColumnClassName?.(header.column.id, 'header')
)}
style={getHeaderSizeStyle(header, applyHeaderSize)}
>
{renderHeaderContent(header)}
{shouldRenderColumnResizer(table, header) && (
<div
role='separator'
aria-orientation='vertical'
aria-label={t('Resize column')}
onDoubleClick={() => header.column.resetSize()}
onMouseDown={header.getResizeHandler()}
onTouchStart={header.getResizeHandler()}
className={cn(
'absolute top-0 right-0 h-full w-2 cursor-col-resize touch-none select-none',
'after:bg-border hover:after:bg-primary after:absolute after:top-2 after:right-0 after:h-[calc(100%-1rem)] after:w-px after:transition-colors',
header.column.getIsResizing() && 'after:bg-primary'
)}
/>
)}

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Resize handle is mouse/touch-only; no keyboard support.

The resizer div (Lines 65-79) uses role='separator' with onMouseDown/onTouchStart only — there's no tabIndex, no onKeyDown, and no aria-valuenow/aria-valuemin/aria-valuemax. Keyboard-only users cannot resize columns at all with this implementation, which is a common headless-table pitfall since TanStack Table intentionally leaves all accessibility to the consumer.

As per coding guidelines, web/default/**/*.{ts,tsx} components must "ensure keyboard operability and sensible focus order, add ARIA attributes when needed."

Consider adding tabIndex={0} plus an onKeyDown handler that nudges header.column.getSize() via arrow keys (calling the table's setColumnSizing or equivalent), and exposing current/min/max via ARIA attributes.

Also applies to: 88-99

🤖 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/data-table-header.tsx` around
lines 58 - 79, The column resizer in data-table-header is mouse/touch-only and
lacks keyboard accessibility. Update the resizer element in DataTableHeader to
be focusable and operable via keyboard by adding a tab stop and an onKeyDown
handler that adjusts the column size using the table/header sizing APIs. Also
expose the current sizing state with appropriate ARIA attributes on the
separator element so keyboard-only users can resize columns.

Source: Coding guidelines

<div
role='separator'
aria-orientation='vertical'
aria-label={t('Resize column')}

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -e json . web/default/src/i18n/locales --exec rg -l '"Resize column"' {}

Repository: QuantumNous/new-api

Length of output: 157


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== locale files ==\n'
fd -a -t f '.*\.json$' web/default/src/i18n/locales

printf '\n== search for Resize column (case-sensitive) ==\n'
rg -n '"Resize column"|Resize column' web/default/src/i18n/locales || true

printf '\n== inspect i18n config ==\n'
fd -a -t f 'i18n' web/default/src | sed -n '1,20p'
rg -n "fallbackLng|lng|keySeparator|nsSeparator|returnNull|returnEmptyString|fallbackNS" web/default/src/i18n web/default/src --glob '!**/*.map' || true

Repository: QuantumNous/new-api

Length of output: 1000


Add Resize column to the locale files. The new aria-label key is missing from web/default/src/i18n/locales/*.json, so it will fall back to the English key in every locale.

🤖 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/data-table-header.tsx` at line 69,
The new aria-label in DataTableHeader uses the literal “Resize column” without a
localized entry, so add this key to the locale JSON files under
web/default/src/i18n/locales and wire it into the existing translation set.
Update the relevant i18n resources used by data-table-header.tsx so the
t('Resize column') lookup resolves in every locale instead of falling back to
English.

Source: Coding guidelines

Comment on lines 294 to +301
manualFiltering,
manualPagination,
manualSorting,
enableColumnResizing: options.enableColumnResizing,
columnResizeMode: 'onChange',
onSortingChange,
onColumnVisibilityChange,
onColumnSizingChange,

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.

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '240,430p' web/default/src/components/data-table/hooks/use-data-table.ts

Repository: QuantumNous/new-api

Length of output: 5038


Defer columnSizing persistence off the resize hot path. columnResizeMode: 'onChange' keeps columnSizing updating during every drag tick, and the effect at lines 384-401 synchronously writes to localStorage on each change. That can cause visible resize jank; debounce the write or flush it only after the drag settles.

🤖 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/hooks/use-data-table.ts` around lines
294 - 301, The current use-data-table setup keeps columnSizing updating on every
drag tick via useDataTable with columnResizeMode set to onChange, and the
persistence effect then writes to localStorage synchronously on every change.
Update the column sizing persistence path in use-data-table so it is debounced
or deferred until resizing settles, instead of writing during the resize hot
path. Keep the change focused around the columnSizing/localStorage effect and
the column resizing handlers so drag updates remain smooth.

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.

1 participant