Skip to content

Refactor/UI: refactor sidebar & format codebase#107

Merged
whhjdi merged 5 commits intoawsl-project:mainfrom
whhjdi:feat/ui
Jan 17, 2026
Merged

Refactor/UI: refactor sidebar & format codebase#107
whhjdi merged 5 commits intoawsl-project:mainfrom
whhjdi:feat/ui

Conversation

@whhjdi
Copy link
Contributor

@whhjdi whhjdi commented Jan 17, 2026

Summary by CodeRabbit

发布说明

  • 新功能

    • 全新侧边栏:更灵活的导航结构与动态路由项和请求入口。
    • 供应商选择流程:新增模板与更完善的创建/选择界面。
    • 路由页面:新增标题与快速创建按钮。
  • UI/UX改进

    • 供应商卡片与模版卡片视觉与交互增强。
    • 请求详情与侧栏展示优化。
    • 主题提供器增加默认设置以改善初始体验。
  • 国际化

    • 增加并完善中/英文翻译词条(含新增供应商相关文案)。

✏️ Tip: You can customize this high-level summary in your review settings.

- Refactor AppSidebar to use object-based configuration
  - Create unified sidebar-config.tsx as single source of truth
  - Add TypeScript discriminated unions for type safety
  - Extract components: RequestsNavItem, ClientRoutesItems
  - Create SidebarRenderer for unified rendering logic
  - Remove deprecated nav-main, nav-management, nav-routes

- Update Prettier configuration
  - Add semicolons (semi: true)
  - Use trailing commas everywhere (trailingComma: all)
  - Increase line width to 100 (printWidth: 100)
  - Always use arrow function parens (arrowParens: always)
  - Add explicit bracket spacing and JSX quote configs

- Reformat all code with new Prettier config

All functionality preserved: streaming badges, marquee animations, section grouping
@coderabbitai
Copy link

coderabbitai bot commented Jan 17, 2026

📝 Walkthrough

Walkthrough

本次变更:用模块化的 AppSidebar + sidebarConfig + SidebarRenderer 替换旧 SidebarNav,并新增侧边栏类型定义;同时在大量文件中统一代码风格(分号、单引号、Prettier 配置)并增加若干 i18n 文案与 UI 小改动(provider 创建流程、Request 详情拆分等)。

Changes

Cohort / File(s) 变更摘要
Prettier 配置
web/.prettierrc.json
更新格式化规则(semi:true、trailingComma:"all"、printWidth:100、arrowParens:"always" 等)
侧边栏新架构
web/src/components/layout/app-sidebar/index.tsx, .../sidebar-config.tsx, .../sidebar-renderer.tsx, .../requests-nav-item.tsx, .../client-routes-items.tsx
新增 AppSidebar、集中 sidebarConfig、SidebarRenderer,新增 RequestsNavItem / ClientRoutesItems,导出别名 SidebarNav 以兼容旧用法
移除旧侧边栏模块
web/src/components/layout/sidebar-nav.tsx, .../nav-main.tsx, .../nav-management.tsx
删除旧的 SidebarNav、NavMain、NavManagement 实现及内联导航定义
布局与导出调整
web/src/components/layout/app-layout.tsx, web/src/components/layout/index.ts, web/src/types/sidebar.ts
app-layout 切换为 AppSidebar,更新布局对话框挂钩;新增侧边栏类型定义(MenuItem/SidebarConfig 等)
UI 组件风格统一
web/src/components/ui/* (button, card, dialog, field, input, select, sheet, tooltip, 等大量文件)
大规模格式化/类型字面量风格调整(双引号→单引号,React.ComponentProps<...> 引号风格统一,分号等),多数为无功能变更
页面与组件调整
web/src/pages/**, web/src/components/**
若干组件格式化与微调;RequestDetailPanel 将 request 视图委托给 RequestDetailView;provider 创建/选择流程引入 i18n;ProviderCard 添加 onClick 支持;部分列表渲染小修(key、文本重排)
i18n 与语言资源
web/src/locales/en.json, web/src/locales/zh.json, web/src/lib/i18n.ts
新增顶级翻译键 addProvider(providers/templates 文案),并修正若干中文标点;i18n 初始化样式调整
样式与静态资源
web/src/index.css
CSS 变量与色彩字面量重排/压缩,@Keyframes 与排版重格式化(视觉语义保留)
Hooks / transport / types 微调
web/src/hooks/*, web/src/lib/transport/*, web/src/lib/theme.ts
主要是格式/逗号/分号调整;ThemeProvider 添加默认参数;transport 接口小的声明/格式调整(无语义变更)
新增类型模块
web/src/types/sidebar.ts
新增 Sidebar 配置与 MenuItem 类型定义(Standard/Custom/DynamicSection)

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant AppLayout
    participant AppSidebar
    participant SidebarRenderer
    participant SidebarConfig

    User->>AppLayout: 打开应用页面
    AppLayout->>AppSidebar: 请求渲染侧边栏
    AppSidebar->>AppSidebar: 获取代理状态与版本
    AppSidebar->>SidebarRenderer: 传入 sidebarConfig
    SidebarRenderer->>SidebarConfig: 读取 sections 列表
    SidebarConfig-->>SidebarRenderer: 返回 sections
    loop 遍历 sections
        SidebarRenderer->>SidebarRenderer: 遍历 items
        alt type == "standard"
            SidebarRenderer->>AppSidebar: 渲染标准导航项 (NavLink/Button)
        else type == "custom"
            SidebarRenderer->>AppSidebar: 渲染自定义组件
        else type == "dynamic-section"
            SidebarRenderer->>AppSidebar: 调用 generator 产出节点
        end
    end
    SidebarRenderer-->>AppSidebar: 返回完整侧边栏节点
    AppSidebar-->>AppLayout: 将侧边栏注入布局
    AppLayout-->>User: 显示页面(含新侧边栏)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 分钟

Possibly related PRs

Suggested reviewers

  • awsl233777
  • liril-net

Poem

🐰 我是长耳小兔,欢跃来报春,
新侧栏列队整齐,配置化更轻盈,
分号齐整声声响,单引号也温柔,
文案添了新章节,翻译键里开花朵,
跳跃一圈又一圈,代码整洁心也宁。

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR标题准确地反映了本次更改的两个主要方面:侧边栏重构和代码库格式化。

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 13

Caution

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

⚠️ Outside diff range comments (5)
web/src/components/ui/sidebar.tsx (1)

154-161: 修复错误的 collapsible 属性命名 'offExamples''off'

collapsible 属性的默认值 'offExamples' 是错误的命名。该值在类型定义(第161行)、CSS 选择器(第219、231、232、290、291、292行)中一致使用。字符串中包含 "Examples" 明确表示这是从示例代码中遗留下来的占位符。应将其更正为 'off',与其他选项 'icon''none' 的命名风格保持一致。

web/src/components/force-project-dialog.tsx (3)

225-231: 硬编码的中文文本破坏了 i18n 一致性

组件已导入 useTranslation 并在其他地方使用 t() 函数,但拒绝按钮的文本是硬编码的中文:

  • "拒绝中..." (Line 225)
  • "拒绝" (Line 230)

这会导致非中文用户看到中文界面文本。

🛠️ 建议修复

首先在 en.jsonzh.json 中添加相应的翻译 key,然后:

                {rejectSession.isPending ? (
                  <>
                    <div className="h-4 w-4 animate-spin rounded-full border-2 border-red-400/30 border-t-red-400" />
-                    <span className="text-sm font-bold">拒绝中...</span>
+                    <span className="text-sm font-bold">{t('sessions.rejecting')}</span>
                  </>
                ) : (
                  <>
                    <X size={16} />
-                    <span className="text-sm font-bold">拒绝</span>
+                    <span className="text-sm font-bold">{t('sessions.reject')}</span>
                  </>
                )}

248-262: 确认按钮同样存在硬编码中文文本

确认按钮的文本也是硬编码的中文:

  • "绑定中..." (Line 250)
  • "确认绑定" (Line 259)
🛠️ 建议修复
                  {updateSessionProject.isPending ? (
                    <>
                      <div className="h-4 w-4 animate-spin rounded-full border-2 border-white/30 border-t-white" />
-                      <span className="text-sm font-bold text-white">绑定中...</span>
+                      <span className="text-sm font-bold text-white">{t('sessions.binding')}</span>
                    </>
                  ) : (
                    <>
                      <FolderOpen
                        size={16}
                        className="text-amber-400 group-hover:text-white transition-colors"
                      />
                      <span className="text-sm font-bold text-amber-400 group-hover:text-white transition-colors">
-                        确认绑定
+                        {t('sessions.confirmBind')}
                      </span>
                    </>
                  )}

267-270: 提示信息硬编码中文

底部警告提示也是硬编码的中文文本。

🛠️ 建议修复
            <div className="flex items-start gap-2 rounded-lg bg-muted/50 p-2.5 text-[11px] text-muted-foreground">
              <AlertCircle size={12} className="mt-0.5 shrink-0" />
-              <p>如果未在规定时间内选择项目,请求将被拒绝。</p>
+              <p>{t('sessions.timeoutWarning')}</p>
            </div>
web/src/pages/providers/components/provider-card.tsx (1)

14-109: 可点击卡片缺少键盘可访问性(role/tabIndex/键盘触发)

AntigravityProviderCardCustomProviderCard 的外层容器是 div,虽然绑定了 onClick 事件并应用了 cursor-pointer 样式表现为按钮,但缺少键盘交互支持。键盘用户和屏幕阅读器用户无法访问这些卡片。建议为两处卡片容器添加 role="button"tabIndex={0} 和 Enter/Space 键处理逻辑。

建议修复
 export function AntigravityProviderCard({ provider, onClick, streamingCount }: ProviderCardProps) {
   const { t } = useTranslation();
+  const handleCardKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
+    if (e.key === 'Enter' || e.key === ' ') {
+      e.preventDefault();
+      onClick();
+    }
+  };
   
   return (
     <div
       onClick={onClick}
+      onKeyDown={handleCardKeyDown}
+      role="button"
+      tabIndex={0}
       className={`bg-muted border border-border rounded-xl p-4 hover:border-accent/30 hover:bg-accent cursor-pointer transition-all relative group ${
 export function CustomProviderCard({ provider, onClick, streamingCount }: ProviderCardProps) {
   const { t } = useTranslation();
+  const handleCardKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
+    if (e.key === 'Enter' || e.key === ' ') {
+      e.preventDefault();
+      onClick();
+    }
+  };
   
   return (
     <div
       onClick={onClick}
+      onKeyDown={handleCardKeyDown}
+      role="button"
+      tabIndex={0}
       className={`bg-muted border border-border rounded-xl p-4 hover:border-accent/30 hover:bg-accent cursor-pointer transition-all relative group ${
🤖 Fix all issues with AI agents
In `@web/src/components/cooldown-details-dialog.tsx`:
- Around line 123-137: The current formatUntilTime uses toLocaleString and then
the code splits that string (untilDateStr.split(' ')) which breaks for locales
that don't separate date/time with a space; update formatUntilTime into two
helpers (e.g., formatDatePart and formatTimePart) or use two Intl.DateTimeFormat
instances to produce the date and time independently (respectively using options
for month/day and for hour/minute/second with hour12 false) and replace the
untilDateStr + split logic with direct calls to these helpers when computing
datePart and timePart for the component; change references to formatUntilTime
and untilDateStr accordingly so no string split is performed.

In `@web/src/components/layout/app-sidebar/client-routes-items.tsx`:
- Around line 13-16: The component-level subscription useStreamingRequests() is
being called inside each ClientNavItem causing duplicate subscriptions; instead
call useStreamingRequests() once in the parent component ClientRoutesItems,
obtain countsByClient (or precompute a streamingCount map), and pass the
relevant value down to ClientNavItem via a prop (e.g., streamingCount or
countsByClient). Remove useStreamingRequests() from ClientNavItem (and any other
child that does the same around lines 51-56), update ClientNavItem's signature
to accept the prop, and ensure all places that rendered ClientNavItem now supply
the precomputed count to avoid repeated subscriptions and duplicate updates.

In `@web/src/components/layout/app-sidebar/requests-nav-item.tsx`:
- Line 15: The active-check using location.pathname.startsWith('/requests') is
too broad and matches paths like /requests-foo; update the logic that sets
isActive (the variable computed from location.pathname) to only match the exact
'/requests' route or routes under that segment by checking for location.pathname
=== '/requests' || location.pathname.startsWith('/requests/'); use the same
check wherever isActive or the pathname prefix check appears to ensure correct
boundary matching.

In `@web/src/components/layout/app-sidebar/sidebar-renderer.tsx`:
- Around line 27-30: The current isActive calculation uses
location.pathname.startsWith(item.to), which misidentifies routes like
"/requests-foo" as matching "/requests"; update the logic in the isActive
computation (referencing isActive, item.activeMatch, location.pathname, item.to)
to enforce a path boundary: when activeMatch !== 'exact' treat a match as true
only if location.pathname === item.to or location.pathname starts with item.to +
'/' (or use a regex that checks for a segment boundary after item.to), so that
only same path or nested paths match and prefixes like "/requests-foo" do not.
- Around line 51-53: 在 sidebar-renderer.tsx 中处理 'dynamic-section' 的分支不要用 <div>
包裹动态内容(会破坏 SidebarMenu 的 <ul> 语义),将该分支改为用 React.Fragment 包裹并保留 key(即在处理
'dynamic-section' 时返回一个带 key 的 Fragment,内部直接渲染 item.generator()),以保证生成的直接子元素不是
<div> 而是符合 <ul> 语义的片段。

In `@web/src/components/layout/index.ts`:
- Around line 2-7: Remove the unnecessary public exports SidebarRenderer,
RequestsNavItem, and ClientRoutesItems from the module's public index (the
export list that currently re-exports SidebarRenderer, RequestsNavItem, and
ClientRoutesItems), leaving only SidebarNav, PageHeader, and NavProxyStatus; if
any of those three were intended to be part of the public API, instead add
documentation or usage examples for SidebarRenderer, RequestsNavItem, and
ClientRoutesItems rather than exporting them from the top-level layout index.

In `@web/src/components/ui/input-group.tsx`:
- Around line 53-58: The click handler currently focuses only an input by using
e.currentTarget.parentElement?.querySelector('input'), so textarea or other
controls with data-slot won't get focus; update the selector to look for the
group control element (e.g.
e.currentTarget.parentElement?.querySelector('[data-slot="input-group-control"]'))
and call .focus() on that element if present (ensure the element is focusable),
keeping the early return when the click is on a button; locate this change in
the onClick handler inside the InputGroup component in input-group.tsx.

In `@web/src/pages/api-tokens/index.tsx`:
- Around line 289-294: The copy button in the tokens table is attempting to copy
token.token but existing tokens returned by getAPITokens() do not include the
plaintext token (per APITokenCreateResult), only tokenPrefix is present; update
the UI in web/src/pages/api-tokens/index.tsx to remove or render the table-row
Copy Button disabled (the onClick handler that calls
navigator.clipboard.writeText(token.token) should be removed/disabled) and
ensure the only functional copy action remains in the token creation dialog
where the full plaintext token is available and already handled.

In `@web/src/pages/providers/components/antigravity-token-import.tsx`:
- Around line 80-116: The interval callback in handleOAuth closes over the stale
oauthStatus value; change the closure to use functional state updates so it
reads the latest status when the popup closes. Specifically, inside the
setInterval callback (where oauthWindowRef.current?.closed is checked), replace
any direct checks of oauthStatus with a functional updater on setOAuthStatus
(and inside that updater, if prev === 'waiting' transition to 'idle' and call
setOAuthState(null)); retain clearInterval(checkWindowClosed) as before and keep
oauthWindowRef usage unchanged.

In `@web/src/pages/providers/components/model-mapping-editor.tsx`:
- Around line 48-55: Trim and validate keys in handleUpdate: compute const
trimmedNew = newKey.trim() and const trimmedOld = oldKey.trim(), and if
trimmedNew is empty or equals trimmedOld just update newValue[trimmedOld] =
newVal (do not delete/rename); if trimmedNew !== trimmedOld and value
hasOwnProperty(trimmedNew) (i.e. duplicate key exists) do not perform the rename
— instead update newValue[trimmedOld] = newVal to avoid overwriting; only when
trimmedNew is non-empty and not a duplicate perform the rename by deleting
newValue[trimmedOld] and setting newValue[trimmedNew] = newVal, then call
onChange(newValue).

In `@web/src/pages/providers/components/provider-create-flow.tsx`:
- Line 220: Remove the stray text node "asdadas" that was accidentally left
inside the JSX of the ProviderCreateFlow component in provider-create-flow.tsx;
locate the JSX return (or render) block in the ProviderCreateFlow
function/component and delete the literal text node so no unexpected text is
rendered in the UI, and run the component to confirm no other leftover debug
strings remain.

In `@web/src/pages/providers/components/select-type-step.tsx`:
- Around line 104-110: 当前 SelectTypeStep 组件中 h3 显示的 "Kiro (Q Developer)" 和 p 显示的
"AWS CodeWhisperer / Q Developer" 是硬编码字符串,需与文件中其余使用的 t(...) 国际化方式保持一致;在
SelectTypeStep 的对应 JSX(含 className="text-headline font-semibold text-foreground
mb-1" 的 h3 和下方 p)将这两处文本替换为调用现有 i18n t(...),使用语义化的翻译 key(例如 provider.kiro.title /
provider.kiro.description 或与项目翻译命名一致的 key),并在项目的语言资源文件中添加这些 key 的翻译条目。 Ensure to
import/use the same t function/hooks already used in the file so runtime
behavior remains unchanged.

In `@web/src/pages/requests/detail/RequestDetailPanel.tsx`:
- Around line 38-45: The formatJSON function incorrectly treats all falsy values
as missing; change the initial check to only treat null/undefined as missing
(e.g., use obj == null or obj === null || obj === undefined) so numeric 0,
false, and empty string are preserved and then keep the existing
try/catch/JSON.stringify behavior to format the value or fall back to
String(obj).
🧹 Nitpick comments (17)
web/src/pages/projects/tabs/overview.tsx (1)

1-154: 可选建议:考虑使用自动化格式工具。

所有更改都是格式调整,没有功能性影响。为了在整个代码库中保持一致的代码风格,可以考虑配置 Prettier 或 ESLint 的格式化规则,以自动化处理此类格式调整。

web/src/pages/sessions.tsx (1)

38-38: 可选优化:考虑使用 useMemo 缓存 projectMap

当前实现每次渲染都会创建新的 Map 对象。虽然对于小数组影响不大,但可以使用 useMemo 进行优化。

♻️ 建议的优化
+import { useState, useEffect, useMemo } from 'react';
-import { useState, useEffect } from 'react';
-  const projectMap = new Map(projects?.map((p) => [p.id, p.name]) ?? []);
+  const projectMap = useMemo(
+    () => new Map(projects?.map((p) => [p.id, p.name]) ?? []),
+    [projects]
+  );
web/src/pages/retry-configs/index.tsx (1)

54-70: 建议去掉重复的 refetch(),避免双重请求

useUpdateRetryConfig/useCreateRetryConfigonSuccess 已经 invalidateQueries,对当前页面活跃查询通常会触发 refetch;这里再手动 refetch() 可能导致重复请求。
请确认你们当前的 React Query 版本里 invalidateQueries 对活跃查询的行为后再精简。

♻️ 建议改动
           onSuccess: () => {
             setHasChanges(false);
-            refetch();
           },
         },
       );
     } else {
       // Create if doesn't exist
       createConfig.mutate(data, {
         onSuccess: () => {
           setHasChanges(false);
-          refetch();
         },
       });
     }
web/src/pages/providers/components/provider-edit-flow.tsx (1)

174-177: 建议统一文案走翻译资源。

这些硬编码文案(如编辑标题、删除、错误提示)如果产品已启用多语言,建议统一使用翻译 key,避免后续 i18n 覆盖不完整。

Also applies to: 181-184, 277-277, 312-316, 321-325

web/src/pages/routing-strategies/index.tsx (1)

107-131: 建议为表单标签补充 htmlFor/id 绑定以提升可访问性
当前 label 与 select 未建立显式关联,屏幕阅读器与点击标签聚焦体验会受影响。

♿️ 建议修改
-                  <label className="mb-1 block text-sm font-medium">Project</label>
+                  <label htmlFor="routing-project" className="mb-1 block text-sm font-medium">
+                    Project
+                  </label>
                   <select
+                    id="routing-project"
                     value={projectID}
                     onChange={(e) => setProjectID(e.target.value)}
                     className="w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-xs focus:border-ring focus:ring-2 focus:ring-ring/50 outline-none"
                   >
...
-                  <label className="mb-1 block text-sm font-medium">Type</label>
+                  <label htmlFor="routing-type" className="mb-1 block text-sm font-medium">
+                    Type
+                  </label>
                   <select
+                    id="routing-type"
                     value={type}
                     onChange={(e) => setType(e.target.value as RoutingStrategyType)}
                     className="w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-xs focus:border-ring focus:ring-2 focus:ring-ring/50 outline-none"
                   >
web/src/pages/model-mappings/index.tsx (1)

152-176: 拖拽排序更新可并行化以降低等待

Line 152-176 当前逐条 await 更新,规则较多时会明显拖慢完成时间。若后端允许并发或提供批量接口,可考虑并行化更新以提升响应。

♻️ 可选优化示例
-      for (let i = 0; i < reordered.length; i++) {
-        const rule = reordered[i];
-        if (rule.priority !== i * 10) {
-          await updateMapping.mutateAsync({
-            id: rule.id,
-            data: {
-              pattern: rule.pattern,
-              target: rule.target,
-              priority: i * 10,
-              isEnabled: rule.isEnabled,
-            },
-          });
-        }
-      }
+      const updates = reordered
+        .map((rule, i) => ({ rule, priority: i * 10 }))
+        .filter(({ rule, priority }) => rule.priority !== priority);
+
+      await Promise.all(
+        updates.map(({ rule, priority }) =>
+          updateMapping.mutateAsync({
+            id: rule.id,
+            data: {
+              pattern: rule.pattern,
+              target: rule.target,
+              priority,
+              isEnabled: rule.isEnabled,
+            },
+          }),
+        ),
+      );
web/src/components/theme-provider.tsx (2)

72-78: useTheme Hook 的错误检查可以更精确

当前检查 context === undefined,但由于 initialState 提供了默认值,context 不会是 undefined。如果组件在 Provider 外部使用,将返回 initialState 而不是抛出错误。

如果需要严格检查,建议使用不同的哨兵值或 null 作为默认 context。


31-33: 此组件为客户端应用,SSR 问题不适用

该文件位于 React SPA(单页应用)中,使用 createRoot() 在客户端挂载,不存在服务端渲染环境。虽然没有 'use client' 指令,但当前架构中 localStorage 访问不会导致错误。

然而,添加防护检查仍是良好实践,若此组件未来在 Next.js SSR 应用中使用,将需要该保护:

🔧 建议的修复方案(可选)
  const [theme, setTheme] = useState<Theme>(
-    () => (localStorage.getItem(storageKey) as Theme) || defaultTheme,
+    () => {
+      if (typeof window === 'undefined') return defaultTheme;
+      return (localStorage.getItem(storageKey) as Theme) || defaultTheme;
+    },
  );
web/src/components/theme-toggle.tsx (2)

26-28: 建议使用国际化字符串

这里的主题选项使用了硬编码的英文字符串("Light"、"Dark"、"System"),而项目中其他组件(如 api-tokens/index.tsx)使用了 useTranslation 进行国际化。

建议保持一致性:

♻️ 建议的重构方案
+import { useTranslation } from 'react-i18next';

 export function ThemeToggle() {
+  const { t } = useTranslation();
   const { setTheme } = useTheme();

   return (
     <DropdownMenu>
       {/* ... */}
       <DropdownMenuContent align="end">
-        <DropdownMenuItem onClick={() => setTheme('light')}>Light</DropdownMenuItem>
-        <DropdownMenuItem onClick={() => setTheme('dark')}>Dark</DropdownMenuItem>
-        <DropdownMenuItem onClick={() => setTheme('system')}>System</DropdownMenuItem>
+        <DropdownMenuItem onClick={() => setTheme('light')}>{t('theme.light')}</DropdownMenuItem>
+        <DropdownMenuItem onClick={() => setTheme('dark')}>{t('theme.dark')}</DropdownMenuItem>
+        <DropdownMenuItem onClick={() => setTheme('system')}>{t('theme.system')}</DropdownMenuItem>
       </DropdownMenuContent>
     </DropdownMenu>
   );
 }

21-21: 屏幕阅读器文本也应国际化

<span className="sr-only">Toggle theme</span> 中的文本同样应使用翻译函数,以确保可访问性功能在所有语言环境下正常工作。

web/src/pages/requests/detail/RequestHeader.tsx (3)

24-33: 日期格式化使用了硬编码的 locale

formatTime 函数硬编码使用 'en-US' locale,而 api-tokens/index.tsx 中使用了 i18n.resolvedLanguage 来动态获取用户语言设置。

建议保持一致性,接受 locale 参数或使用 i18n hook:

♻️ 建议的重构方案
-function formatTime(timestamp: string): string {
+function formatTime(timestamp: string, locale?: string): string {
   const date = new Date(timestamp);
-  return date.toLocaleString('en-US', {
+  return date.toLocaleString(locale ?? 'en-US', {
     month: 'short',
     day: 'numeric',
     hour: '2-digit',
     minute: '2-digit',
     second: '2-digit',
   });
 }

92-144: 统计标签未国际化

"Duration"、"Input"、"Output"、"Cache Read"、"Cache Write"、"Cost" 等标签都是硬编码的英文字符串。如果项目需要支持多语言,建议使用翻译函数。


14-22: 成本格式化使用了硬编码的美元符号

formatCost 函数使用硬编码的 $ 符号,如果需要支持其他货币或地区格式,建议使用 Intl.NumberFormat 进行货币格式化。

web/src/types/sidebar.ts (1)

1-31: 建议显式引入 React 类型,避免依赖全局命名空间。

当前文件使用 React.ComponentType / React.ReactNode 但未显式引入 React 类型;若项目未启用全局 React 命名空间或有 ESLint no-undef 规则,会报错。建议改为 type-only import。

♻️ 建议修改
-import type { LucideIcon } from 'lucide-react';
+import type { ComponentType, ReactNode } from 'react';
+import type { LucideIcon } from 'lucide-react';
@@
 export interface CustomMenuItem {
   type: 'custom';
   key: string;
-  component: React.ComponentType;
+  component: ComponentType;
 }
@@
 export interface DynamicSectionMenuItem {
   type: 'dynamic-section';
   key: string;
-  generator: () => React.ReactNode;
+  generator: () => ReactNode;
 }
web/src/pages/settings/index.tsx (1)

219-227: 可选优化:考虑对超时输入添加防抖。

handleTimeoutChange 在每次输入变化时直接调用 API,可能会导致频繁的网络请求。可以考虑像 DataRetentionSection 一样使用草稿状态模式,或添加防抖处理。

♻️ 建议的防抖实现
// 使用 useDebouncedCallback 或类似方案
const handleTimeoutChange = useDebouncedCallback(async (value: string) => {
  const numValue = parseInt(value, 10);
  if (numValue >= 5 && numValue <= 300) {
    await updateSetting.mutateAsync({
      key: 'force_project_timeout',
      value: value,
    });
  }
}, 500);
web/src/pages/routes/index.tsx (1)

48-52: 考虑使用自定义确认对话框替代 confirm()

原生 confirm() 可以正常工作,但与应用的整体 UI 风格不一致。可以考虑在后续迭代中使用自定义的确认对话框组件以保持界面一致性。

web/src/pages/requests/detail/RequestDetailView.tsx (1)

135-158: 提取 JSON 解析逻辑以减少重复与双重解析
Line 135-158 和 Line 217-240:同一段 body 被解析两次(CopyButton + 展示),建议提取 helper,避免重复 try/catch 和多次 JSON.parse

♻️ 建议重构
 export function RequestDetailView({
   request,
   activeTab,
   setActiveTab,
   formatJSON,
   formatCost,
   projectName,
   sessionInfo,
   projectMap,
   tokenName,
 }: RequestDetailViewProps) {
   const { t } = useTranslation();
+  const formatBody = (raw?: string) => {
+    if (!raw) return '';
+    try {
+      return formatJSON(JSON.parse(raw));
+    } catch {
+      return raw;
+    }
+  };
   return (
...
-                      content={(() => {
-                        try {
-                          return formatJSON(JSON.parse(request.requestInfo.body));
-                        } catch {
-                          return request.requestInfo.body;
-                        }
-                      })()}
+                      content={formatBody(request.requestInfo.body)}
...
-                      {(() => {
-                        try {
-                          return formatJSON(JSON.parse(request.requestInfo.body));
-                        } catch {
-                          return request.requestInfo.body;
-                        }
-                      })()}
+                      {formatBody(request.requestInfo.body)}
...
-                      content={(() => {
-                        try {
-                          return formatJSON(JSON.parse(request.responseInfo.body));
-                        } catch {
-                          return request.responseInfo.body;
-                        }
-                      })()}
+                      content={formatBody(request.responseInfo.body)}
...
-                      {(() => {
-                        try {
-                          return formatJSON(JSON.parse(request.responseInfo.body));
-                        } catch {
-                          return request.responseInfo.body;
-                        }
-                      })()}
+                      {formatBody(request.responseInfo.body)}

Also applies to: 217-240

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7dc2809 and 7ecedd3.

📒 Files selected for processing (115)
  • web/.prettierrc.json
  • web/src/components/cooldown-details-dialog.tsx
  • web/src/components/force-project-dialog.tsx
  • web/src/components/layout/app-layout.tsx
  • web/src/components/layout/app-sidebar/client-routes-items.tsx
  • web/src/components/layout/app-sidebar/index.tsx
  • web/src/components/layout/app-sidebar/requests-nav-item.tsx
  • web/src/components/layout/app-sidebar/sidebar-config.tsx
  • web/src/components/layout/app-sidebar/sidebar-renderer.tsx
  • web/src/components/layout/header.tsx
  • web/src/components/layout/index.ts
  • web/src/components/layout/nav-main.tsx
  • web/src/components/layout/nav-management.tsx
  • web/src/components/layout/nav-proxy-status.tsx
  • web/src/components/layout/page-header.tsx
  • web/src/components/layout/sidebar-nav.tsx
  • web/src/components/provider-details-dialog.tsx
  • web/src/components/routes/ClientTypeRoutesContent.tsx
  • web/src/components/theme-provider.tsx
  • web/src/components/theme-toggle.tsx
  • web/src/components/ui/alert-dialog.tsx
  • web/src/components/ui/badge.tsx
  • web/src/components/ui/button.tsx
  • web/src/components/ui/card.tsx
  • web/src/components/ui/combobox.tsx
  • web/src/components/ui/dialog.tsx
  • web/src/components/ui/dropdown-menu.tsx
  • web/src/components/ui/field.tsx
  • web/src/components/ui/index.ts
  • web/src/components/ui/input-group.tsx
  • web/src/components/ui/input.tsx
  • web/src/components/ui/label.tsx
  • web/src/components/ui/model-input.tsx
  • web/src/components/ui/resizable.tsx
  • web/src/components/ui/select.tsx
  • web/src/components/ui/separator.tsx
  • web/src/components/ui/sheet.tsx
  • web/src/components/ui/sidebar.tsx
  • web/src/components/ui/skeleton.tsx
  • web/src/components/ui/streaming-badge.tsx
  • web/src/components/ui/switch.tsx
  • web/src/components/ui/table.tsx
  • web/src/components/ui/tabs.tsx
  • web/src/components/ui/textarea.tsx
  • web/src/components/ui/tooltip.tsx
  • web/src/hooks/queries/index.ts
  • web/src/hooks/queries/use-proxy.ts
  • web/src/hooks/queries/use-requests.ts
  • web/src/hooks/queries/use-routes.ts
  • web/src/hooks/queries/use-routing-strategies.ts
  • web/src/hooks/queries/use-settings.ts
  • web/src/hooks/use-cooldowns.ts
  • web/src/hooks/use-mobile.tsx
  • web/src/hooks/use-pending-session.ts
  • web/src/hooks/use-streaming.ts
  • web/src/index.css
  • web/src/lib/i18n.ts
  • web/src/lib/theme.ts
  • web/src/lib/transport/context.tsx
  • web/src/lib/transport/factory.ts
  • web/src/lib/transport/http-transport.ts
  • web/src/lib/transport/index.ts
  • web/src/lib/transport/interface.ts
  • web/src/lib/transport/types.ts
  • web/src/lib/utils.ts
  • web/src/locales/en.json
  • web/src/locales/zh.json
  • web/src/main.tsx
  • web/src/pages/api-tokens/index.tsx
  • web/src/pages/client-routes/components/provider-row.tsx
  • web/src/pages/client-routes/index.tsx
  • web/src/pages/console/index.tsx
  • web/src/pages/login.tsx
  • web/src/pages/model-mappings/index.tsx
  • web/src/pages/overview.tsx
  • web/src/pages/projects/detail.tsx
  • web/src/pages/projects/index.tsx
  • web/src/pages/projects/tabs/overview.tsx
  • web/src/pages/projects/tabs/requests.tsx
  • web/src/pages/projects/tabs/routes.tsx
  • web/src/pages/projects/tabs/sessions.tsx
  • web/src/pages/providers/components/antigravity-coming-soon.tsx
  • web/src/pages/providers/components/antigravity-provider-view.tsx
  • web/src/pages/providers/components/antigravity-token-import.tsx
  • web/src/pages/providers/components/clients-config-section.tsx
  • web/src/pages/providers/components/kiro-provider-view.tsx
  • web/src/pages/providers/components/kiro-token-import.tsx
  • web/src/pages/providers/components/model-mapping-editor.tsx
  • web/src/pages/providers/components/provider-card.tsx
  • web/src/pages/providers/components/provider-create-flow.tsx
  • web/src/pages/providers/components/provider-edit-flow.tsx
  • web/src/pages/providers/components/provider-row.tsx
  • web/src/pages/providers/components/select-type-step.tsx
  • web/src/pages/providers/index.tsx
  • web/src/pages/providers/types.ts
  • web/src/pages/requests/detail.tsx
  • web/src/pages/requests/detail/RequestDetailPanel.tsx
  • web/src/pages/requests/detail/RequestDetailView.tsx
  • web/src/pages/requests/detail/RequestHeader.tsx
  • web/src/pages/requests/detail/RequestSidebar.tsx
  • web/src/pages/requests/detail/components/CopyAsCurlButton.tsx
  • web/src/pages/requests/detail/components/CopyButton.tsx
  • web/src/pages/requests/detail/components/DiffButton.tsx
  • web/src/pages/requests/detail/components/DiffModal.tsx
  • web/src/pages/requests/detail/components/EmptyState.tsx
  • web/src/pages/requests/detail/components/index.ts
  • web/src/pages/requests/index.tsx
  • web/src/pages/retry-configs/index.tsx
  • web/src/pages/routes/form.tsx
  • web/src/pages/routes/index.tsx
  • web/src/pages/routing-strategies/index.tsx
  • web/src/pages/sessions.tsx
  • web/src/pages/settings/index.tsx
  • web/src/pages/stats/index.tsx
  • web/src/types/sidebar.ts
💤 Files with no reviewable changes (3)
  • web/src/components/layout/nav-management.tsx
  • web/src/components/layout/sidebar-nav.tsx
  • web/src/components/layout/nav-main.tsx
🧰 Additional context used
🧬 Code graph analysis (67)
web/src/lib/utils.ts (1)
launcher/script.js (1)
  • seconds (121-121)
web/src/components/layout/app-sidebar/sidebar-config.tsx (1)
web/src/types/sidebar.ts (1)
  • SidebarConfig (50-52)
web/src/pages/requests/detail/components/DiffButton.tsx (1)
web/src/pages/requests/detail/components/index.ts (1)
  • DiffButton (4-4)
web/src/lib/transport/interface.ts (2)
web/src/lib/transport/types.ts (2)
  • Session (78-85)
  • AntigravityQuotaData (335-340)
internal/domain/model.go (1)
  • Session (113-126)
web/src/pages/projects/tabs/sessions.tsx (1)
web/src/components/ui/index.ts (1)
  • TableCell (15-15)
web/src/hooks/queries/use-routing-strategies.ts (2)
web/src/lib/query-client.ts (1)
  • queryClient (3-14)
web/src/hooks/queries/index.ts (1)
  • routingStrategyKeys (62-62)
web/src/pages/requests/detail/components/CopyButton.tsx (1)
web/src/pages/requests/detail/components/index.ts (1)
  • CopyButton (1-1)
web/src/pages/providers/components/select-type-step.tsx (1)
web/src/pages/providers/types.ts (3)
  • ProviderFormData (165-172)
  • PROVIDER_TYPE_CONFIGS (27-60)
  • quickTemplates (90-147)
web/src/components/layout/app-sidebar/requests-nav-item.tsx (4)
web/src/hooks/use-streaming.ts (1)
  • useStreamingRequests (29-114)
web/src/components/ui/sidebar.tsx (3)
  • SidebarMenuItem (688-688)
  • SidebarMenuButton (687-687)
  • SidebarMenuBadge (686-686)
web/src/components/ui/streaming-badge.tsx (1)
  • StreamingBadge (27-81)
web/src/components/layout/sidebar-nav.tsx (1)
  • RequestsNavItem (57-89)
web/src/pages/routes/form.tsx (6)
web/src/lib/transport/types.ts (2)
  • Route (89-101)
  • ClientType (8-8)
web/src/lib/transport/http-transport.ts (2)
  • createRoute (163-166)
  • updateRoute (168-171)
web/src/hooks/queries/use-routes.ts (2)
  • useCreateRoute (35-44)
  • useUpdateRoute (47-58)
web/src/hooks/queries/use-providers.ts (1)
  • useProviders (20-25)
web/src/hooks/queries/use-projects.ts (1)
  • useProjects (20-25)
web/src/lib/theme.ts (1)
  • ClientType (25-25)
web/src/pages/projects/index.tsx (5)
web/src/hooks/queries/index.ts (2)
  • useProjects (22-22)
  • useCreateProject (25-25)
web/src/hooks/queries/use-projects.ts (2)
  • useProjects (20-25)
  • useCreateProject (46-55)
web/src/lib/transport/http-transport.ts (1)
  • createProject (137-140)
web/src/components/ui/button.tsx (1)
  • Button (58-58)
web/src/components/ui/index.ts (1)
  • Button (2-2)
web/src/components/ui/input.tsx (2)
web/src/components/ui/index.ts (1)
  • Input (23-23)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/components/force-project-dialog.tsx (5)
web/src/lib/transport/types.ts (1)
  • NewSessionPendingEvent (284-288)
web/src/lib/transport/http-transport.ts (2)
  • updateSessionProject (188-197)
  • rejectSession (199-204)
web/src/hooks/queries/use-sessions.ts (2)
  • useUpdateSessionProject (24-37)
  • useRejectSession (40-49)
web/src/components/icons/client-icons.tsx (1)
  • getClientColor (44-46)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/pages/console/index.tsx (4)
web/src/lib/transport/factory.ts (1)
  • getTransport (79-102)
web/src/lib/transport/index.ts (1)
  • getTransport (78-78)
web/src/pages/requests/detail/components/EmptyState.tsx (1)
  • EmptyState (8-15)
web/src/pages/requests/detail/components/index.ts (1)
  • EmptyState (5-5)
web/src/components/ui/resizable.tsx (1)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/components/ui/alert-dialog.tsx (1)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/pages/model-mappings/index.tsx (4)
web/src/lib/transport/types.ts (2)
  • ModelMapping (369-384)
  • ModelMappingInput (387-398)
internal/domain/model.go (1)
  • ModelMapping (492-517)
web/src/components/ui/model-input.tsx (1)
  • ModelInput (161-411)
web/src/hooks/queries/index.ts (6)
  • useModelMappings (90-90)
  • useCreateModelMapping (91-91)
  • useUpdateModelMapping (92-92)
  • useDeleteModelMapping (93-93)
  • useClearAllModelMappings (94-94)
  • useResetModelMappingsToDefaults (95-95)
web/src/components/layout/app-sidebar/index.tsx (2)
web/src/components/layout/app-sidebar/sidebar-config.tsx (1)
  • sidebarConfig (21-128)
web/src/components/theme-toggle.tsx (1)
  • ThemeToggle (11-32)
web/src/components/layout/app-sidebar/sidebar-renderer.tsx (2)
web/src/types/sidebar.ts (2)
  • SidebarConfig (50-52)
  • MenuItem (36-36)
web/src/components/ui/sidebar.tsx (6)
  • SidebarMenuItem (688-688)
  • SidebarMenuButton (687-687)
  • SidebarGroup (677-677)
  • SidebarGroupLabel (680-680)
  • SidebarGroupContent (679-679)
  • SidebarMenu (684-684)
web/src/components/ui/streaming-badge.tsx (1)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/pages/projects/tabs/requests.tsx (2)
web/src/lib/transport/types.ts (1)
  • Project (63-70)
internal/domain/model.go (1)
  • Project (98-111)
web/src/pages/requests/detail.tsx (7)
web/src/hooks/queries/index.ts (8)
  • useProxyRequest (75-75)
  • useProxyUpstreamAttempts (76-76)
  • useProviders (8-8)
  • useProjects (22-22)
  • useSessions (45-45)
  • useRoutes (33-33)
  • useAPITokens (101-101)
  • useProxyRequestUpdates (77-77)
web/src/hooks/queries/use-requests.ts (3)
  • useProxyRequest (42-48)
  • useProxyUpstreamAttempts (51-57)
  • useProxyRequestUpdates (60-130)
web/src/hooks/queries/use-providers.ts (1)
  • useProviders (20-25)
web/src/hooks/queries/use-projects.ts (1)
  • useProjects (20-25)
web/src/hooks/queries/use-sessions.ts (1)
  • useSessions (16-21)
web/src/hooks/queries/use-routes.ts (1)
  • useRoutes (18-23)
web/src/hooks/queries/use-api-tokens.ts (1)
  • useAPITokens (18-23)
web/src/components/ui/skeleton.tsx (1)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/hooks/queries/use-settings.ts (2)
web/src/lib/transport/types.ts (1)
  • ModelMappingInput (387-398)
web/src/lib/transport/factory.ts (1)
  • getTransport (79-102)
web/src/components/layout/app-layout.tsx (4)
web/src/components/layout/index.ts (1)
  • AppLayout (1-1)
web/src/hooks/use-pending-session.ts (1)
  • usePendingSession (9-49)
web/src/hooks/queries/use-settings.ts (1)
  • useSettings (15-20)
web/src/components/ui/sidebar.tsx (1)
  • SidebarProvider (693-693)
web/src/pages/stats/index.tsx (1)
web/src/components/ui/tooltip.tsx (1)
  • Tooltip (56-56)
web/src/pages/requests/detail/components/CopyAsCurlButton.tsx (7)
web/src/lib/transport/index.ts (1)
  • RequestInfo (30-30)
web/src/lib/transport/types.ts (1)
  • RequestInfo (147-152)
internal/domain/model.go (1)
  • RequestInfo (161-166)
web/src/pages/requests/detail/components/index.ts (1)
  • CopyAsCurlButton (2-2)
web/src/hooks/queries/index.ts (1)
  • useSetting (87-87)
web/src/hooks/queries/use-settings.ts (1)
  • useSetting (22-28)
web/src/components/ui/index.ts (1)
  • Button (2-2)
web/src/components/ui/card.tsx (1)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/pages/settings/index.tsx (3)
web/src/components/theme-provider.tsx (1)
  • useTheme (72-78)
web/src/hooks/queries/use-settings.ts (2)
  • useSettings (15-20)
  • useUpdateSetting (30-40)
web/src/lib/transport/http-transport.ts (1)
  • updateSetting (323-328)
web/src/components/routes/ClientTypeRoutesContent.tsx (6)
web/src/lib/theme.ts (4)
  • ClientType (25-25)
  • getClientColor (198-200)
  • getProviderColor (153-155)
  • ProviderType (9-20)
web/src/lib/transport/index.ts (2)
  • ClientType (8-8)
  • Provider (9-9)
web/src/lib/transport/types.ts (2)
  • ClientType (8-8)
  • Provider (43-51)
web/src/hooks/queries/use-routes.ts (6)
  • useRoutes (18-23)
  • useCreateRoute (35-44)
  • useToggleRoute (73-88)
  • useDeleteRoute (61-70)
  • useUpdateRoutePositions (91-108)
  • routeKeys (9-15)
web/src/hooks/use-streaming.ts (1)
  • useStreamingRequests (29-114)
web/src/pages/client-routes/types.ts (1)
  • ProviderConfigItem (4-10)
web/src/pages/projects/detail.tsx (2)
web/src/lib/query-client.ts (1)
  • queryClient (3-14)
web/src/lib/transport/http-transport.ts (1)
  • deleteProject (147-149)
web/src/components/ui/textarea.tsx (1)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/lib/transport/http-transport.ts (4)
web/src/lib/transport/types.ts (11)
  • Session (78-85)
  • RoutingStrategy (134-141)
  • CursorPaginationParams (248-254)
  • CursorPaginationResult (257-264)
  • ProxyRequest (168-202)
  • ProxyUpstreamAttempt (213-238)
  • ProviderStats (307-319)
  • AntigravityBatchValidationResult (350-352)
  • AntigravityQuotaData (335-340)
  • KiroQuotaData (418-429)
  • AuthVerifyResult (474-478)
internal/domain/model.go (5)
  • Session (113-126)
  • RoutingStrategy (329-342)
  • ProxyRequest (174-236)
  • ProxyUpstreamAttempt (238-285)
  • ProviderStats (399-419)
internal/service/admin.go (1)
  • CursorPaginationResult (353-358)
internal/adapter/provider/kiro/service.go (1)
  • KiroQuotaData (32-43)
web/src/components/theme-toggle.tsx (2)
web/src/components/theme-provider.tsx (1)
  • useTheme (72-78)
web/src/components/ui/dropdown-menu.tsx (3)
  • DropdownMenuItem (235-235)
  • DropdownMenuContent (232-232)
  • DropdownMenu (229-229)
web/src/components/ui/label.tsx (1)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/components/ui/combobox.tsx (2)
web/src/components/ui/input-group.tsx (2)
  • InputGroup (138-138)
  • InputGroupInput (142-142)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/components/layout/header.tsx (3)
web/src/components/theme-provider.tsx (1)
  • useTheme (72-78)
web/src/components/ui/button.tsx (1)
  • Button (58-58)
web/src/components/ui/index.ts (1)
  • Button (2-2)
web/src/components/ui/badge.tsx (1)
web/src/components/ui/index.ts (1)
  • badgeVariants (20-20)
web/src/components/layout/app-sidebar/client-routes-items.tsx (2)
web/src/hooks/use-streaming.ts (1)
  • useStreamingRequests (29-114)
web/src/components/icons/client-icons.tsx (2)
  • getClientName (51-53)
  • allClientTypes (105-105)
web/src/pages/requests/detail/components/DiffModal.tsx (1)
web/src/pages/requests/detail/components/index.ts (1)
  • DiffModal (3-3)
web/src/hooks/queries/use-requests.ts (1)
web/src/lib/query-client.ts (1)
  • queryClient (3-14)
web/src/pages/retry-configs/index.tsx (2)
web/src/hooks/queries/use-retry-configs.ts (3)
  • useRetryConfigs (18-23)
  • useUpdateRetryConfig (47-58)
  • useCreateRetryConfig (35-44)
web/src/lib/transport/types.ts (1)
  • RetryConfig (112-122)
web/src/pages/providers/components/antigravity-provider-view.tsx (3)
web/src/lib/transport/index.ts (4)
  • Provider (9-9)
  • AntigravityModelQuota (45-45)
  • AntigravityQuotaData (46-46)
  • getTransport (78-78)
web/src/lib/transport/types.ts (3)
  • Provider (43-51)
  • AntigravityModelQuota (329-333)
  • AntigravityQuotaData (335-340)
web/src/lib/transport/factory.ts (1)
  • getTransport (79-102)
web/src/components/ui/tabs.tsx (1)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/pages/projects/tabs/overview.tsx (4)
web/src/lib/transport/http-transport.ts (1)
  • updateProject (142-145)
web/src/lib/query-client.ts (1)
  • queryClient (3-14)
web/src/hooks/queries/index.ts (1)
  • projectKeys (21-21)
web/src/hooks/queries/use-projects.ts (1)
  • projectKeys (9-17)
web/src/pages/providers/components/provider-create-flow.tsx (6)
web/src/pages/providers/types.ts (1)
  • CreateStep (175-175)
web/src/hooks/queries/use-providers.ts (1)
  • useCreateProvider (37-47)
web/src/lib/theme.ts (1)
  • ClientType (25-25)
web/src/lib/transport/index.ts (2)
  • ClientType (8-8)
  • CreateProviderData (13-13)
web/src/lib/transport/types.ts (2)
  • ClientType (8-8)
  • CreateProviderData (54-59)
web/src/pages/providers/components/clients-config-section.tsx (1)
  • ClientsConfigSection (13-67)
web/src/pages/providers/components/clients-config-section.tsx (4)
web/src/components/icons/client-icons.tsx (1)
  • ClientIcon (65-100)
web/src/components/ui/index.ts (2)
  • Switch (43-43)
  • Input (23-23)
web/src/components/ui/switch.tsx (1)
  • Switch (30-30)
web/src/components/ui/input.tsx (1)
  • Input (20-20)
web/src/components/ui/switch.tsx (2)
web/src/components/ui/index.ts (1)
  • Switch (43-43)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/pages/providers/components/provider-row.tsx (4)
web/src/lib/transport/index.ts (4)
  • Provider (9-9)
  • ProviderStats (32-32)
  • AntigravityQuotaData (46-46)
  • KiroQuotaData (55-55)
web/src/lib/transport/types.ts (4)
  • Provider (43-51)
  • ProviderStats (307-319)
  • AntigravityQuotaData (335-340)
  • KiroQuotaData (418-429)
web/src/pages/providers/types.ts (1)
  • getProviderTypeConfig (63-65)
web/src/hooks/queries/use-providers.ts (2)
  • useAntigravityQuota (99-108)
  • useKiroQuota (111-120)
web/src/components/ui/dropdown-menu.tsx (1)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/hooks/queries/use-proxy.ts (1)
web/src/hooks/queries/index.ts (1)
  • proxyKeys (81-81)
web/src/pages/projects/tabs/routes.tsx (8)
web/src/lib/transport/types.ts (3)
  • ClientType (8-8)
  • Project (63-70)
  • Route (89-101)
web/src/components/icons/client-icons.tsx (3)
  • ClientIcon (65-100)
  • getClientName (51-53)
  • getClientColor (44-46)
web/src/hooks/queries/index.ts (3)
  • useRoutes (33-33)
  • useUpdateProject (26-26)
  • projectKeys (21-21)
web/src/hooks/queries/use-routes.ts (1)
  • useRoutes (18-23)
web/src/hooks/use-streaming.ts (1)
  • useStreamingRequests (29-114)
web/src/lib/transport/http-transport.ts (1)
  • updateProject (142-145)
web/src/hooks/queries/use-projects.ts (2)
  • useUpdateProject (58-69)
  • projectKeys (9-17)
web/src/components/ui/streaming-badge.tsx (1)
  • StreamingBadge (27-81)
web/src/pages/routes/index.tsx (5)
web/src/hooks/queries/use-routes.ts (2)
  • useRoutes (18-23)
  • useDeleteRoute (61-70)
web/src/hooks/queries/use-providers.ts (1)
  • useProviders (20-25)
web/src/lib/transport/http-transport.ts (1)
  • deleteRoute (173-175)
web/src/lib/transport/types.ts (1)
  • Route (89-101)
web/src/pages/routes/form.tsx (1)
  • RouteForm (15-168)
web/src/pages/providers/components/kiro-provider-view.tsx (5)
web/src/lib/transport/index.ts (3)
  • Provider (9-9)
  • KiroQuotaData (55-55)
  • getTransport (78-78)
web/src/lib/transport/types.ts (2)
  • Provider (43-51)
  • KiroQuotaData (418-429)
internal/domain/model.go (1)
  • Provider (76-96)
internal/adapter/provider/kiro/service.go (1)
  • KiroQuotaData (32-43)
web/src/components/icons/client-icons.tsx (1)
  • ClientIcon (65-100)
web/src/components/ui/dialog.tsx (1)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/components/ui/input-group.tsx (4)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/components/ui/button.tsx (1)
  • Button (58-58)
web/src/components/ui/input.tsx (1)
  • Input (20-20)
web/src/components/ui/textarea.tsx (1)
  • Textarea (18-18)
web/src/components/ui/model-input.tsx (4)
web/src/lib/transport/index.ts (1)
  • Provider (9-9)
web/src/lib/transport/types.ts (1)
  • Provider (43-51)
internal/domain/model.go (1)
  • Provider (76-96)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/pages/providers/components/kiro-token-import.tsx (3)
web/src/lib/transport/index.ts (3)
  • CreateProviderData (13-13)
  • KiroTokenValidationResult (54-54)
  • getTransport (78-78)
web/src/lib/transport/types.ts (2)
  • CreateProviderData (54-59)
  • KiroTokenValidationResult (402-416)
internal/adapter/provider/kiro/service.go (1)
  • KiroTokenValidationResult (15-29)
web/src/components/cooldown-details-dialog.tsx (3)
web/src/lib/transport/index.ts (1)
  • Cooldown (59-59)
web/src/lib/transport/types.ts (1)
  • Cooldown (458-466)
web/src/hooks/use-cooldowns.ts (1)
  • useCooldowns (6-122)
web/src/pages/providers/components/model-mapping-editor.tsx (1)
web/src/components/ui/model-input.tsx (1)
  • ModelInput (161-411)
web/src/components/ui/separator.tsx (1)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/pages/providers/components/provider-card.tsx (4)
web/src/lib/transport/index.ts (1)
  • Provider (9-9)
web/src/lib/transport/types.ts (1)
  • Provider (43-51)
web/src/hooks/use-cooldowns.ts (1)
  • useCooldowns (6-122)
web/src/lib/transport/http-transport.ts (1)
  • clearCooldown (439-441)
web/src/components/ui/field.tsx (2)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/components/ui/label.tsx (1)
  • Label (18-18)
web/src/pages/sessions.tsx (4)
web/src/hooks/queries/use-sessions.ts (2)
  • useSessions (16-21)
  • useUpdateSessionProject (24-37)
web/src/lib/transport/types.ts (1)
  • Session (78-85)
web/src/lib/transport/http-transport.ts (1)
  • updateSessionProject (188-197)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/pages/client-routes/components/provider-row.tsx (7)
web/src/pages/client-routes/types.ts (1)
  • ProviderConfigItem (4-10)
web/src/lib/theme.ts (3)
  • ClientType (25-25)
  • getProviderColorVar (139-141)
  • ProviderType (9-20)
web/src/lib/transport/index.ts (1)
  • ClientType (8-8)
web/src/lib/transport/types.ts (1)
  • ClientType (8-8)
web/src/hooks/use-cooldowns.ts (1)
  • useCooldowns (6-122)
web/src/hooks/queries/use-providers.ts (1)
  • useAntigravityQuota (99-108)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/components/ui/sheet.tsx (1)
web/src/lib/utils.ts (1)
  • cn (8-10)
web/src/lib/transport/types.ts (1)
internal/domain/model.go (1)
  • Provider (76-96)

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines 123 to 137
const formatUntilTime = (until: string) => {
const date = new Date(until)
const date = new Date(until);
return date.toLocaleString(i18n.resolvedLanguage ?? i18n.language, {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
})
}
});
};

const untilDateStr = formatUntilTime(cooldown.untilTime)
const [datePart, timePart] = untilDateStr.split(' ')
const untilDateStr = formatUntilTime(cooldown.untilTime);
const [datePart, timePart] = untilDateStr.split(' ');

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

避免通过字符串 split 解析本地化时间

Line 123-137 依赖 toLocaleString()split(' ') 拆分日期/时间,在部分语言环境(含非空格分隔符或 NBSP)下会拿到错误片段。建议直接分别格式化日期与时间,避免解析字符串。

🔧 建议修改
-  const formatUntilTime = (until: string) => {
-    const date = new Date(until);
-    return date.toLocaleString(i18n.resolvedLanguage ?? i18n.language, {
-      month: '2-digit',
-      day: '2-digit',
-      hour: '2-digit',
-      minute: '2-digit',
-      second: '2-digit',
-      hour12: false,
-    });
-  };
-
-  const untilDateStr = formatUntilTime(cooldown.untilTime);
-  const [datePart, timePart] = untilDateStr.split(' ');
+  const formatUntilTimeParts = (until: string) => {
+    const date = new Date(until);
+    const locale = i18n.resolvedLanguage ?? i18n.language;
+    return {
+      datePart: date.toLocaleDateString(locale, { month: '2-digit', day: '2-digit' }),
+      timePart: date.toLocaleTimeString(locale, {
+        hour: '2-digit',
+        minute: '2-digit',
+        second: '2-digit',
+        hour12: false,
+      }),
+    };
+  };
+
+  const { datePart, timePart } = formatUntilTimeParts(cooldown.untilTime);
🤖 Prompt for AI Agents
In `@web/src/components/cooldown-details-dialog.tsx` around lines 123 - 137, The
current formatUntilTime uses toLocaleString and then the code splits that string
(untilDateStr.split(' ')) which breaks for locales that don't separate date/time
with a space; update formatUntilTime into two helpers (e.g., formatDatePart and
formatTimePart) or use two Intl.DateTimeFormat instances to produce the date and
time independently (respectively using options for month/day and for
hour/minute/second with hour12 false) and replace the untilDateStr + split logic
with direct calls to these helpers when computing datePart and timePart for the
component; change references to formatUntilTime and untilDateStr accordingly so
no string split is performed.

Comment on lines +27 to +30
const isActive =
item.activeMatch === 'exact'
? location.pathname === item.to
: location.pathname.startsWith(item.to);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

避免前缀匹配误判激活状态
Line 27-30:startsWith 会让 /requests-foo 误匹配 /requests。建议加路径边界以降低误判。

🛠️ 建议修改
-      const isActive =
-        item.activeMatch === 'exact'
-          ? location.pathname === item.to
-          : location.pathname.startsWith(item.to);
+      const isActive =
+        item.activeMatch === 'exact'
+          ? location.pathname === item.to
+          : location.pathname === item.to || location.pathname.startsWith(`${item.to}/`);
📝 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 isActive =
item.activeMatch === 'exact'
? location.pathname === item.to
: location.pathname.startsWith(item.to);
const isActive =
item.activeMatch === 'exact'
? location.pathname === item.to
: location.pathname === item.to || location.pathname.startsWith(`${item.to}/`);
🤖 Prompt for AI Agents
In `@web/src/components/layout/app-sidebar/sidebar-renderer.tsx` around lines 27 -
30, The current isActive calculation uses location.pathname.startsWith(item.to),
which misidentifies routes like "/requests-foo" as matching "/requests"; update
the logic in the isActive computation (referencing isActive, item.activeMatch,
location.pathname, item.to) to enforce a path boundary: when activeMatch !==
'exact' treat a match as true only if location.pathname === item.to or
location.pathname starts with item.to + '/' (or use a regex that checks for a
segment boundary after item.to), so that only same path or nested paths match
and prefixes like "/requests-foo" do not.

Comment on lines 38 to 45
function formatJSON(obj: unknown): string {
if (!obj) return '-'
if (!obj) return '-';
try {
return JSON.stringify(obj, null, 2)
return JSON.stringify(obj, null, 2);
} catch {
return String(obj)
return String(obj);
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

修正 formatJSON 对 falsy 值的处理。

if (!obj) 会把 0/false/'' 误显示为 -,当请求/响应体为 JSON 字面量时会丢信息。建议仅对 null/undefined 返回占位符。

🛠️ 建议修复
function formatJSON(obj: unknown): string {
-  if (!obj) return '-';
+  if (obj === null || obj === undefined) return '-';
  try {
    return JSON.stringify(obj, null, 2);
  } catch {
    return String(obj);
  }
}
📝 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
function formatJSON(obj: unknown): string {
if (!obj) return '-'
if (!obj) return '-';
try {
return JSON.stringify(obj, null, 2)
return JSON.stringify(obj, null, 2);
} catch {
return String(obj)
return String(obj);
}
}
function formatJSON(obj: unknown): string {
if (obj === null || obj === undefined) return '-';
try {
return JSON.stringify(obj, null, 2);
} catch {
return String(obj);
}
}
🤖 Prompt for AI Agents
In `@web/src/pages/requests/detail/RequestDetailPanel.tsx` around lines 38 - 45,
The formatJSON function incorrectly treats all falsy values as missing; change
the initial check to only treat null/undefined as missing (e.g., use obj == null
or obj === null || obj === undefined) so numeric 0, false, and empty string are
preserved and then keep the existing try/catch/JSON.stringify behavior to format
the value or fall back to String(obj).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
web/src/pages/providers/components/model-mapping-editor.tsx (1)

104-112: i18n 不一致:新增映射区域的 placeholder 未使用翻译。

第 71、79 行使用了 t('modelMapping.requestModel')t('modelMapping.mappedModel'),但这里的 placeholder 仍是硬编码的英文字符串。为保持一致性,应使用相同的翻译 key。

🔧 建议修复
       <ModelInput
         value={newFrom}
         onChange={setNewFrom}
-        placeholder="Request Model"
+        placeholder={t('modelMapping.requestModel')}
         className="flex-1"
         disabled={disabled}
       />
       <ArrowRight size={16} className="text-muted-foreground shrink-0" />
       <ModelInput
         value={newTo}
         onChange={setNewTo}
-        placeholder="Mapped Model"
+        placeholder={t('modelMapping.mappedModel')}
         className="flex-1"
         disabled={disabled}
         providers={targetProviders}
       />
🤖 Fix all issues with AI agents
In `@web/src/components/layout/app-sidebar/requests-nav-item.tsx`:
- Around line 37-40: The current conditional mounts
SidebarMenuBadge/StreamingBadge only when total > 0 which unmounts
StreamingBadge immediately when total hits 0 and breaks its hideDelay; instead
always render SidebarMenuBadge and StreamingBadge (remove the total > 0
short-circuit) and pass total as a prop (e.g., count or visible flag) so
StreamingBadge can animate/hide with its hideDelay, or alternatively move the
delay logic into the parent component that owns total; also ensure
SidebarMenuBadge handles empty-child styling so it doesn't leave unwanted layout
when count is 0.

In `@web/src/pages/providers/components/model-mapping-editor.tsx`:
- Around line 129-134: The empty-state paragraph is hardcoded in English;
replace the literal text in the JSX that renders when entries.length === 0 with
a call to the i18n helper (t) using a new key like
"modelMapping.noMappingsConfigured" (update the JSX that contains the
conditional rendering of entries to call t(...)), and add the corresponding
translation entry "modelMapping.noMappingsConfigured" to the locale translation
file(s) so the placeholder is translated across locales.
♻️ Duplicate comments (1)
web/src/pages/providers/components/model-mapping-editor.tsx (1)

48-60: 修复已确认:handleUpdate 现在正确处理边界情况。

之前的审查意见已解决 — 现在 key 和 value 会被 trim,空值会被拒绝,重命名时若目标 key 已存在也会阻止覆盖。实现正确。

🧹 Nitpick comments (1)
web/src/pages/providers/components/select-type-step.tsx (1)

89-118: Kiro 卡片样式与其他 provider 卡片不一致

Kiro 卡片使用 rounded-lg(第 93 行),而 Antigravity 和 Custom 卡片均使用 rounded-xl。此外还缺少以下样式:

  • overflow-hidden
  • duration-200 过渡效果
  • focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2
  • hover:border-accent/30 hover:shadow-sm

建议统一样式以保持视觉一致性。

♻️ 建议修改
                <Button
                  onClick={() => onSelectType('kiro')}
                  variant="ghost"
-                  className={`group p-0 rounded-lg border text-left transition-all h-auto w-full ${
+                  className={`group p-0 rounded-xl border text-left h-auto w-full overflow-hidden transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 ${
                    formData.type === 'kiro'
-                      ? 'border-provider-kiro bg-provider-kiro/10'
-                      : 'border-border bg-card hover:bg-muted'
+                      ? 'border-provider-kiro bg-provider-kiro/10 shadow-sm'
+                      : 'border-border bg-card hover:bg-muted hover:border-accent/30 hover:shadow-sm'
                  }`}
                >
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ecedd3 and f5d5e73.

📒 Files selected for processing (7)
  • web/src/components/layout/app-sidebar/client-routes-items.tsx
  • web/src/components/layout/app-sidebar/requests-nav-item.tsx
  • web/src/components/layout/app-sidebar/sidebar-renderer.tsx
  • web/src/pages/providers/components/antigravity-token-import.tsx
  • web/src/pages/providers/components/model-mapping-editor.tsx
  • web/src/pages/providers/components/provider-create-flow.tsx
  • web/src/pages/providers/components/select-type-step.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • web/src/components/layout/app-sidebar/client-routes-items.tsx
  • web/src/pages/providers/components/provider-create-flow.tsx
  • web/src/components/layout/app-sidebar/sidebar-renderer.tsx
🧰 Additional context used
🧬 Code graph analysis (4)
web/src/pages/providers/components/select-type-step.tsx (1)
web/src/pages/providers/types.ts (3)
  • ProviderFormData (165-172)
  • PROVIDER_TYPE_CONFIGS (27-60)
  • quickTemplates (90-147)
web/src/pages/providers/components/model-mapping-editor.tsx (1)
web/src/components/ui/model-input.tsx (1)
  • ModelInput (161-411)
web/src/components/layout/app-sidebar/requests-nav-item.tsx (3)
web/src/hooks/use-streaming.ts (1)
  • useStreamingRequests (29-114)
web/src/components/ui/sidebar.tsx (3)
  • SidebarMenuItem (688-688)
  • SidebarMenuButton (687-687)
  • SidebarMenuBadge (686-686)
web/src/components/ui/streaming-badge.tsx (1)
  • StreamingBadge (27-81)
web/src/pages/providers/components/antigravity-token-import.tsx (3)
web/src/lib/transport/index.ts (4)
  • CreateProviderData (13-13)
  • AntigravityTokenValidationResult (47-47)
  • AntigravityOAuthResult (49-49)
  • getTransport (78-78)
web/src/lib/transport/types.ts (3)
  • CreateProviderData (54-59)
  • AntigravityTokenValidationResult (342-348)
  • AntigravityOAuthResult (354-364)
web/src/lib/transport/factory.ts (1)
  • getTransport (79-102)
🔇 Additional comments (13)
web/src/pages/providers/components/antigravity-token-import.tsx (4)

1-33: 类型与签名格式化到位,未引入行为变更。
整体改动为一致化分号与排版,逻辑不受影响。


50-119: OAuth 订阅与弹窗流程保持稳定。
改动集中在格式与细节排版,流程一致。


121-211: Token 校验与创建流程清晰,改动安全。
保持原有校验与异常处理路径,未引入新风险。


213-624: UI 文案与样式调整一致。
展示层更改合理,未影响交互逻辑。

web/src/pages/providers/components/select-type-step.tsx (7)

1-13: LGTM!

导入语句整洁有序,正确引入了 useTranslation 用于国际化支持。


30-34: LGTM!

可见 provider 数量计算和动态 grid 列数逻辑正确,useTranslation hook 使用规范。


36-50: LGTM!

Header 区域正确使用了 i18n 翻译函数,布局响应式处理得当。


60-87: LGTM!

Antigravity 卡片的 i18n 集成、样式和交互状态处理完善,包含了完整的 focus-visible 和 hover 状态。


120-148: LGTM!

Custom 卡片的实现与 Antigravity 保持一致,i18n 和样式处理完善。


163-186: LGTM!

模板区域的 grid 布局响应式配置合理,空模板卡片正确使用了 i18n 翻译。


188-250: LGTM!

模板渲染逻辑处理得当:

  • 正确使用条件表达式实现 i18n fallback(template.nameKey ? t(template.nameKey) : template.name
  • 预计算 templateNametemplateDescription 提高可读性
  • 选中状态的视觉反馈完善
web/src/pages/providers/components/model-mapping-editor.tsx (1)

30-46: LGTM!

handleAddhandleRemove 的实现简洁正确:

  • handleAdd 正确检查空值和重复 key
  • handleRemove 通过创建新对象避免直接修改原 value
web/src/components/layout/app-sidebar/requests-nav-item.tsx (1)

15-16: 路由激活判断更稳健
Line 15-16 的边界判断可以避免误匹配 /requests-foo,符合预期。

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +37 to +40
{total > 0 && (
<SidebarMenuBadge>
<StreamingBadge count={total} color={color} />
</SidebarMenuBadge>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

StreamingBadge 的延迟隐藏被父级条件短路
Line 37-40 只在 total > 0 时挂载会导致 StreamingBadgehideDelay 失效(total 归零后立即卸载)。若希望避免闪烁,建议保持组件挂载,或把延迟逻辑上移到父级。

💡 建议修改(保持组件挂载以启用 hideDelay)
-      {total > 0 && (
-        <SidebarMenuBadge>
-          <StreamingBadge count={total} color={color} />
-        </SidebarMenuBadge>
-      )}
+      <SidebarMenuBadge>
+        <StreamingBadge count={total} color={color} />
+      </SidebarMenuBadge>

SidebarMenuBadge 在无子节点时仍占位,请确认是否需要在其内部处理空内容样式或把延迟逻辑移到父级。

📝 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
{total > 0 && (
<SidebarMenuBadge>
<StreamingBadge count={total} color={color} />
</SidebarMenuBadge>
<SidebarMenuBadge>
<StreamingBadge count={total} color={color} />
</SidebarMenuBadge>
🤖 Prompt for AI Agents
In `@web/src/components/layout/app-sidebar/requests-nav-item.tsx` around lines 37
- 40, The current conditional mounts SidebarMenuBadge/StreamingBadge only when
total > 0 which unmounts StreamingBadge immediately when total hits 0 and breaks
its hideDelay; instead always render SidebarMenuBadge and StreamingBadge (remove
the total > 0 short-circuit) and pass total as a prop (e.g., count or visible
flag) so StreamingBadge can animate/hide with its hideDelay, or alternatively
move the delay logic into the parent component that owns total; also ensure
SidebarMenuBadge handles empty-child styling so it doesn't leave unwanted layout
when count is 0.

Comment on lines 129 to 134
{entries.length === 0 && (
<p className="text-xs text-muted-foreground">
No model mappings configured. Add mappings to transform request models
before sending to upstream.
No model mappings configured. Add mappings to transform request models before sending to
upstream.
</p>
)}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

i18n 缺失:空状态提示文本未国际化。

此处的提示信息是硬编码的英文,与其他已翻译的 placeholder 不一致。建议使用 t() 函数进行国际化处理。

🔧 建议修复
     {entries.length === 0 && (
       <p className="text-xs text-muted-foreground">
-        No model mappings configured. Add mappings to transform request models before sending to
-        upstream.
+        {t('modelMapping.noMappingsConfigured')}
       </p>
     )}

需要在对应的翻译文件中添加 modelMapping.noMappingsConfigured 的翻译条目。

📝 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
{entries.length === 0 && (
<p className="text-xs text-muted-foreground">
No model mappings configured. Add mappings to transform request models
before sending to upstream.
No model mappings configured. Add mappings to transform request models before sending to
upstream.
</p>
)}
{entries.length === 0 && (
<p className="text-xs text-muted-foreground">
{t('modelMapping.noMappingsConfigured')}
</p>
)}
🤖 Prompt for AI Agents
In `@web/src/pages/providers/components/model-mapping-editor.tsx` around lines 129
- 134, The empty-state paragraph is hardcoded in English; replace the literal
text in the JSX that renders when entries.length === 0 with a call to the i18n
helper (t) using a new key like "modelMapping.noMappingsConfigured" (update the
JSX that contains the conditional rendering of entries to call t(...)), and add
the corresponding translation entry "modelMapping.noMappingsConfigured" to the
locale translation file(s) so the placeholder is translated across locales.

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