feat: add Claude provider frontend support#292
Conversation
Add full frontend support for the Claude provider type, complementing the backend added in PR #291. Users can now create, view, and manage Claude providers through the UI with OAuth login and manual token import. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... 📒 Files selected for processing (3)
✏️ Tip: You can disable in-progress messages and the fortune message in your review settings. Tip CodeRabbit can generate a title for your PR based on the changes.Add 📝 Walkthrough概述该PR为应用添加了完整的Claude提供商支持,包括CSS变量、TypeScript类型定义、HTTP传输层集成、UI组件和国际化配置。支持Claude通过OAuth和令牌验证流程进行身份验证。 变更
Sequence Diagram(s)sequenceDiagram
participant User as 用户
participant UI as ClaudeTokenImport
participant Backend as 后端
participant OAuth as Claude OAuth
participant WS as WebSocket
participant DB as 数据库
rect rgba(100, 150, 200, 0.5)
Note over User,DB: OAuth流程
User->>UI: 点击"使用Anthropic登录"
UI->>Backend: startClaudeOAuth()
Backend->>Backend: 生成state、获取authURL
Backend-->>UI: { authURL, state }
UI->>UI: 打开弹窗窗口
UI->>OAuth: 重定向至authURL
User->>OAuth: 在Claude OAuth界面登录
OAuth->>Backend: 重定向回调URL(code, state)
User->>UI: 输入回调URL (或自动捕获)
UI->>Backend: exchangeClaudeOAuthCallback(code, state)
Backend->>OAuth: 交换获取令牌
Backend->>DB: 创建提供商
Backend-->>UI: ClaudeOAuthResult
UI->>WS: 订阅claude_oauth_result
WS-->>UI: 事件到达(state匹配)
UI->>UI: 关闭弹窗、显示成功
end
rect rgba(150, 100, 200, 0.5)
Note over User,DB: 令牌导入流程
User->>UI: 切换至"令牌导入"标签
User->>UI: 粘贴refreshToken、输入email
UI->>Backend: validateClaudeToken(refreshToken)
Backend->>Backend: 验证令牌有效性
Backend-->>UI: ClaudeTokenValidationResult
UI->>UI: 显示验证结果
User->>UI: 点击"创建提供商"
UI->>Backend: 使用验证数据创建提供商
Backend->>DB: 保存提供商配置
Backend-->>UI: 创建成功
UI->>UI: 导航至提供商列表
end
估计代码审查工作量🎯 3 (中等) | ⏱️ ~25 分钟 可能相关的PR
建议标签
建议审查者
诗歌
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/lib/theme.ts`:
- Around line 21-22: ProviderType 已新增 'claude' 但函数 getProviderDisplayName
未为该枚举值提供映射导致回退显示原始类型字符串;在 getProviderDisplayName 中为 ProviderType 'claude'
添加合适的显示名映射(例如 "Claude" 或团队命名约定的全称),确保返回值与其他 provider 的显示一致,保持 UI 文案统一并避免显示原始
type 文本。
In `@web/src/pages/providers/components/claude-provider-view.tsx`:
- Around line 150-163: The ModelInput fields (pattern/target) currently call
handleUpdateMapping on every keystroke, causing request storms and out-of-order
writes; change them to edit locally and only call handleUpdateMapping when the
user blurs or explicitly saves (or at minimum debounce input and serialize
requests). Concretely: keep a local component state for mapping edits tied to
the ModelInput value, update that state onChange, and invoke
handleUpdateMapping(mapping, {...}) only on onBlur or save; if you add debounce,
ensure the debounced updater serializes requests (e.g., by awaiting previous
update or cancelling prior promises) and respect isPending to prevent
overlapping writes.
- Around line 303-307: Replace the invalid inline CSS backgroundColor:
`${CLAUDE_COLOR}15` (where CLAUDE_COLOR equals "var(--provider-claude)") with a
Tailwind arbitrary color class that applies the CSS variable with opacity, e.g.
remove the style prop and add a class like
"bg-[color:var(--provider-claude)]/15" to the same element (the div that wraps
Sparkles in claude-provider-view.tsx); apply the same change for the analogous
element in claude-token-import.tsx so both use the Tailwind arbitrary color +
opacity syntax instead of string-concatenating "15".
In `@web/src/pages/providers/components/claude-token-import.tsx`:
- Around line 303-306: The inline style using backgroundColor:
`${CLAUDE_COLOR}15` produces invalid CSS; locate the two occurrences where
backgroundColor is set using CLAUDE_COLOR (the JSX around the Sparkles icon in
claude-token-import) and either (A) add the missing Tailwind CSS utility
definitions for provider-claude (and the other missing providers: claude,
antigravity, kiro, codex) in the same style sheet where other provider
backgrounds are defined so you can use a utility like bg-provider-claude/15, or
(B) replace the invalid template with valid CSS that uses the CSS variable
correctly (for example use an rgb/from var(...) with alpha or compute rgba from
the CSS var) so backgroundColor becomes a valid color string; ensure both
occurrences are updated consistently.
- Around line 168-180: The code opens an OAuth popup with oauthWindowRef.current
= window.open(...) and then starts a setInterval stored in checkWindowClosed
without checking for a null popup or ensuring the interval is cleared on all
exit paths; update the logic in the component (around oauthWindowRef usage and
the checkWindowClosed interval) to first test if window.open returned a non-null
value and handle the popup-blocked case (e.g., setPopupClosed(true) or surface
an error), store the interval id in a ref or state (not a local var) so it can
be cleared reliably, and ensure you clearInterval(checkWindowClosed) whenever
the popup is detected closed, on component unmount, and when you manually close
the popup; reference oauthWindowRef, checkWindowClosed (or convert to
checkWindowClosedRef), and setPopupClosed to implement these changes.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
web/src/index.cssweb/src/lib/theme.tsweb/src/lib/transport/http-transport.tsweb/src/lib/transport/index.tsweb/src/lib/transport/interface.tsweb/src/lib/transport/types.tsweb/src/locales/en.jsonweb/src/locales/zh.jsonweb/src/pages/providers/components/claude-provider-view.tsxweb/src/pages/providers/components/claude-token-import.tsxweb/src/pages/providers/components/provider-edit-flow.tsxweb/src/pages/providers/components/select-type-step.tsxweb/src/pages/providers/create-layout.tsxweb/src/pages/providers/hooks/use-provider-navigation.tsweb/src/pages/providers/index.tsxweb/src/pages/providers/types.ts
📜 Review details
🧰 Additional context used
🧬 Code graph analysis (7)
web/src/pages/providers/types.ts (1)
web/src/lib/theme.ts (1)
getProviderColorVar(374-376)
web/src/pages/providers/components/claude-token-import.tsx (6)
web/src/pages/providers/hooks/use-provider-navigation.ts (1)
useProviderNavigation(3-16)web/src/lib/transport/http-transport.ts (1)
createProvider(111-114)web/src/lib/transport/index.ts (4)
ClaudeTokenValidationResult(62-62)ClaudeOAuthResult(63-63)getTransport(108-108)CreateProviderData(13-13)web/src/lib/transport/types.ts (3)
ClaudeTokenValidationResult(573-581)ClaudeOAuthResult(583-592)CreateProviderData(91-97)web/src/pages/providers/types.ts (1)
CLAUDE_COLOR(97-97)web/src/lib/utils.ts (1)
cn(8-10)
web/src/pages/providers/components/provider-edit-flow.tsx (1)
web/src/pages/providers/components/claude-provider-view.tsx (1)
ClaudeProviderView(215-462)
web/src/lib/transport/interface.ts (1)
web/src/lib/transport/types.ts (2)
ClaudeTokenValidationResult(573-581)ClaudeOAuthResult(583-592)
web/src/lib/transport/http-transport.ts (2)
web/src/lib/transport/index.ts (1)
ClaudeTokenValidationResult(62-62)web/src/lib/transport/types.ts (1)
ClaudeTokenValidationResult(573-581)
web/src/pages/providers/create-layout.tsx (3)
web/src/lib/transport/types.ts (1)
Route(127-139)internal/domain/model.go (1)
Route(263-287)internal/repository/sqlite/models.go (2)
Route(97-106)Route(108-108)
web/src/pages/providers/components/select-type-step.tsx (2)
web/src/pages/providers/hooks/use-provider-navigation.ts (1)
useProviderNavigation(3-16)web/src/pages/providers/types.ts (1)
PROVIDER_TYPE_CONFIGS(31-80)
🔇 Additional comments (13)
web/src/index.css (1)
86-86: Claude 主题色与 Tailwind 映射补充完整,设计令牌接入正确。这两处改动保持了 provider 颜色变量与
@theme inline映射的一致性,便于后续统一消费。Also applies to: 370-370
web/src/locales/en.json (1)
253-255: Claude 英文文案覆盖面完整,键位组织与现有结构一致。类型标签、OAuth/Token 导入流程、错误提示与 Add Provider 入口文案都已补齐。
Also applies to: 406-458, 1119-1122
web/src/locales/zh.json (1)
253-255: Claude 中文本地化补充完整,与英文键结构保持对齐。新增文案覆盖了类型标识、导入流程、错误文案和创建入口,整体一致性良好。
Also applies to: 406-458, 1118-1121
web/src/pages/providers/index.tsx (1)
67-67: Provider 分组新增claude正确,列表渲染路径打通。这能确保 Claude provider 在页面中进入明确分组,不会被回退到
custom。web/src/pages/providers/components/provider-edit-flow.tsx (1)
44-44: Claude 编辑分支接入方式一致且可维护。导入与条件分发实现清晰,
DeleteConfirmModal的行为也与其他 provider 分支保持一致。Also applies to: 554-572
web/src/pages/providers/create-layout.tsx (1)
7-7: Claude 创建路由接入正确,创建入口链路完整。
path="claude"与组件导入匹配,符合当前创建布局的路由组织方式。Also applies to: 19-19
web/src/lib/transport/index.ts (1)
61-63: Claude 传输类型的统一导出补充正确。新增
ProviderConfigClaude、ClaudeTokenValidationResult、ClaudeOAuthResult后,transport 类型出口更加完整。web/src/pages/providers/hooks/use-provider-navigation.ts (1)
12-12: Claude 创建页导航入口补充正确。新增跳转路径与创建流程路由一致,改动清晰且低风险。
web/src/lib/transport/types.ts (1)
60-67: Claude 传输层类型扩展完整且一致。配置类型、OAuth/Token 结果类型和 WS 事件类型都已对齐新增能力,结构设计保持了现有风格。
Also applies to: 75-75, 327-327, 571-592
web/src/pages/providers/types.ts (1)
15-15: Provider 类型系统接入 Claude 的改动设计良好。类型联合、展示配置和创建步骤均已同步扩展,避免了前后流程断层。
Also applies to: 57-64, 97-97, 225-225, 245-246
web/src/pages/providers/components/select-type-step.tsx (1)
22-35: Claude 选择卡片与路由分支接入正确。选择态、文案与跳转行为都与现有 Provider 卡片保持一致,实现可靠。
Also applies to: 173-205
web/src/lib/transport/interface.ts (1)
40-41: Transport 接口对 Claude 能力的抽象补齐到位。方法集合覆盖 token 校验、OAuth 启动/交换、provider 信息刷新,接口边界清晰。
Also applies to: 172-176
web/src/lib/transport/http-transport.ts (1)
547-577: Claude HTTP API 对接实现完整。端点覆盖面与接口声明一致,返回类型与调用形态也保持了现有传输层风格。
| <ModelInput | ||
| value={mapping.pattern} | ||
| onChange={(pattern) => handleUpdateMapping(mapping, { pattern })} | ||
| placeholder={t('modelMappings.matchPattern')} | ||
| disabled={isPending} | ||
| className="flex-1 min-w-0 h-8 text-sm" | ||
| /> | ||
| <ArrowRight className="h-4 w-4 text-muted-foreground shrink-0" /> | ||
| <ModelInput | ||
| value={mapping.target} | ||
| onChange={(target) => handleUpdateMapping(mapping, { target })} | ||
| placeholder={t('modelMappings.targetModel')} | ||
| disabled={isPending} | ||
| className="flex-1 min-w-0 h-8 text-sm" |
There was a problem hiding this comment.
模型映射“每次键入即写入”会导致请求风暴与写入乱序。
当前输入一个字符就会触发一次更新请求;并发下旧请求可能晚到并覆盖新值。建议改为“本地编辑 + onBlur/显式保存时提交”,至少加防抖和请求串行化。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/pages/providers/components/claude-provider-view.tsx` around lines 150
- 163, The ModelInput fields (pattern/target) currently call handleUpdateMapping
on every keystroke, causing request storms and out-of-order writes; change them
to edit locally and only call handleUpdateMapping when the user blurs or
explicitly saves (or at minimum debounce input and serialize requests).
Concretely: keep a local component state for mapping edits tied to the
ModelInput value, update that state onChange, and invoke
handleUpdateMapping(mapping, {...}) only on onBlur or save; if you add debounce,
ensure the debounced updater serializes requests (e.g., by awaiting previous
update or cancelling prior promises) and respect isPending to prevent
overlapping writes.
There was a problem hiding this comment.
This is a false positive. The ModelInput component is dialog-based — it opens a modal, user searches/selects a model, and onChange only fires when the user explicitly clicks a model or presses Enter to confirm. It does NOT fire on every keystroke. The internal search state is local to the component. No request storm occurs.
There was a problem hiding this comment.
@Bowl42, thank you for the clarification! You're absolutely right—I misunderstood how the ModelInput component works. I incorrectly assumed it was a standard text input that fires onChange on every keystroke, but as you've explained, it's actually a dialog-based component that only triggers onChange when the user explicitly confirms a selection.
I apologize for the false positive and appreciate you taking the time to explain the component's behavior. 🙏
/)/)
(๑•́ ₃ •̀๑) learned something new!
✏️ Learnings added
Learnt from: Bowl42
Repo: awsl-project/maxx PR: 292
File: web/src/pages/providers/components/claude-provider-view.tsx:150-163
Timestamp: 2026-03-02T17:16:15.099Z
Learning: The `ModelInput` component in `@/components/ui/model-input` is dialog-based (opens a modal for model selection), not a standard text input. Its `onChange` callback only fires when the user explicitly selects a model (by clicking or pressing Enter), not on every keystroke. The internal search state is local to the component, so there is no risk of request storms when used with immediate update handlers.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
- Add 'claude' mapping to getProviderDisplayName in theme.ts
- Replace invalid CSS `${CLAUDE_COLOR}15` with Tailwind `bg-provider-claude/15`
class in claude-provider-view.tsx and claude-token-import.tsx
- Add popup null check for window.open() in OAuth flow
- Store setInterval in ref (oauthPollRef) for proper cleanup on unmount
- Clear interval in cancel handler to prevent memory leaks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
claudeprovider type, complementing the backend OAuth/adapter added in PR feat: add Claude provider adapter with OAuth support #291Changes
claudetoProviderType,--provider-claudeCSS variable (coral color)ProviderConfigClaude,ClaudeTokenValidationResult,ClaudeOAuthResulttypes; 4 API methods (validateClaudeToken,startClaudeOAuth,exchangeClaudeOAuthCallback,refreshClaudeProviderInfo) with HTTP implementations calling/api/claude/*claudeinPROVIDER_TYPE_CONFIGS(icon: Sparkles, account-based), navigation, routingClaudeTokenImport(OAuth + token import),ClaudeProviderView(detail view with email, org ID, token expiry, model mappings, error cooldown)Test plan
tsc --noEmit+vite build)🤖 Generated with Claude Code
Summary by CodeRabbit