增强配置表单输入警告提示 - #768
Conversation
📝 WalkthroughWalkthrough新增多语言警告文本、行内警告组件与若干验证/日期工具,并在用户表单、系统设置与提供者表单中集成这些警告显示;同时添加对应单元测试并调整若干测试用例与测试辅助 mock。 Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 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 |
Summary of ChangesHello @tesgth032, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 此拉取请求通过在各种配置表单中为潜在的错误或问题输入提供即时、非阻塞的警告反馈,从而增强了用户体验。它解决了 API 密钥、系统配额设置和用户过期日期中常见的输入问题,提高了数据完整性并减少了用户错误,同时不阻止表单提交。 Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
| {shouldWarnQuotaLeasePercentZero(quotaLeasePercent5h) && ( | ||
| <InlineWarning>{t("quotaLease.warnings.leasePercentZero")}</InlineWarning> | ||
| )} |
There was a problem hiding this comment.
Additional Comments (2)
Prompt To Fix With AIThis is a comment left during a code review.
Path: src/app/[locale]/settings/config/_components/system-settings-form.tsx
Line: 625:632
Comment:
**Invalid number input yields NaN**
`quotaDbRefreshIntervalSeconds` is set via `Number(e.target.value)`. When the user clears the numeric input, `e.target.value` becomes `""`, so this state becomes `0` (or `NaN` depending on browser/value), which then gets submitted in `saveSystemSettings({ quotaDbRefreshIntervalSeconds })`. This can bypass the intended “0 means don’t warn” behavior and may persist an unintended value to the backend. Consider guarding the state update / submit so empty input doesn’t turn into a numeric value.
How can I resolve this? If you propose a fix, please make it concise.
The default value uses Prompt To Fix With AIThis is a comment left during a code review.
Path: src/app/[locale]/dashboard/_components/user/forms/user-form.tsx
Line: 102:105
Comment:
**UTC date string mismatch**
The default value uses `user.expiresAt.toISOString().split("T")[0]`, which is UTC-based. For users in negative timezones (or when `expiresAt` isn’t midnight UTC), this can show the wrong day in the date picker, and combined with the new local end-of-day parsing can shift the effective expiration by a day. This should format the initial `expiresAt` in local date (or the app’s chosen timezone) rather than UTC ISO.
How can I resolve this? If you propose a fix, please make it concise. |
There was a problem hiding this comment.
Code Review Summary
No significant issues identified in this PR. The implementation adds client-side warning hints for provider settings, system quota/lease configuration, and user expiration dates. The code is well-structured with clean separation between validation logic, UI components, and i18n. The parseYmdToLocalEndOfDay refactoring correctly fixes the UTC date parsing bug described in the PR.
PR Size: M
- Lines changed: 432 (406 additions, 26 deletions)
- Files changed: 25
Review Coverage
- Logic and correctness - Clean
- Security (OWASP Top 10) - Clean
- Error handling - Clean
- Type safety - Clean
- Documentation accuracy - Clean
- Test coverage - Adequate
- Code clarity - Good
Notes
- All 5 i18n languages (en, ja, ru, zh-CN, zh-TW) have consistent key additions across
dashboard.json,settings/config.json, andsettings/providers/form/key.json. - The
parseYmdToLocalEndOfDayfunction correctly avoids thenew Date("YYYY-MM-DD")UTC parsing pitfall by constructing the date from local components. It returnsnullfor invalid input (safer than the oldtoEndOfDaywhich would produce an invalidDateobject). - Validation functions (
detectApiKeyWarnings,shouldWarnQuota*) are pure, well-tested, and correctly handle edge cases (JSON credentials, NaN, empty strings). - The
InlineWarningcomponent correctly usesaria-hidden="true"on the decorative icon while keeping the text accessible.
Automated review by Claude AI
|
已根据审阅意见做了补充修正:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/app/`[locale]/dashboard/_components/user/forms/user-form.tsx:
- Line 126: parseYmdToLocalEndOfDay can return null for invalid date strings,
which currently causes expiresAt to be silently set to null and sent to the
server; locate the two usages of parseYmdToLocalEndOfDay (the assignment that
sets expiresAt from data.expiresAt and the similar occurrence around line 144)
and change the submit flow to validate the parser result: if
parseYmdToLocalEndOfDay(data.expiresAt) === null, block submission and surface a
user-facing validation error (or retain the original value instead of replacing
it with null), ensuring you update the form validation/state handling to prevent
silently clearing the expiry when parsing fails.
🧹 Nitpick comments (4)
tests/unit/lib/provider-endpoints/probe.test.ts (1)
26-33: 测试辅助函数抽取合理,DRY 改进良好。将重复的 circuit breaker mock 逻辑统一到
createCircuitBreakerMock中,减少了大量样板代码,各测试用例的 override 用法也清晰一致。一个小建议:
overrides的类型Partial<Record<string, unknown>>较为宽松,可以考虑用更具体的类型来获得更好的自动补全和类型安全。可选的类型收窄
-function createCircuitBreakerMock(overrides: Partial<Record<string, unknown>> = {}) { +interface CircuitBreakerMock { + getEndpointCircuitStateSync: ReturnType<typeof vi.fn>; + resetEndpointCircuit: ReturnType<typeof vi.fn>; + recordEndpointFailure: ReturnType<typeof vi.fn>; +} + +function createCircuitBreakerMock(overrides: Partial<CircuitBreakerMock> = {}) { return { getEndpointCircuitStateSync: vi.fn(() => "closed"), resetEndpointCircuit: vi.fn(async () => {}), recordEndpointFailure: vi.fn(async () => {}), ...overrides, }; }tests/unit/dashboard/user-form-expiry-clear-ui.test.tsx (2)
75-75: 测试辅助函数中的中文硬编码错误信息。
未找到按钮: ${text}作为测试内部的throw信息不属于用户可见字符串,此处无需国际化,但考虑到编码规范中要求所有用户可见字符串使用 i18n,建议统一使用英文以保持测试代码的一致性和可读性(尤其在 CI 日志中)。建议修改
- throw new Error(`未找到按钮: ${text}`); + throw new Error(`Button not found: ${text}`);
80-85: 测试描述使用了中文字符串。
describe和test的标题为中文。虽然测试描述不是"用户可见字符串"(不需要 i18n),但在多语言团队协作中英文描述更易于 CI 输出阅读和问题定位。这属于风格偏好,仅作为可选建议。src/app/[locale]/settings/config/_components/system-settings-form.tsx (1)
631-641: 即时 clamp 可能导致输入体验不佳。当用户清空输入框准备重新输入时,值会立即跳回
1,导致难以正常编辑。例如用户想输入15:全选删除 → 变为1,再按5→ 变为15(此时前面多了一个1,实际结果可能是15或115,取决于光标位置)。建议参考
quotaLeaseCapUsd的模式,将中间态存储为字符串,仅在失焦或提交时做 clamp/解析。可选:改用字符串缓存 + onBlur clamp
- const [quotaDbRefreshIntervalSeconds, setQuotaDbRefreshIntervalSeconds] = useState( - initialSettings.quotaDbRefreshIntervalSeconds ?? 10 + const [quotaDbRefreshIntervalStr, setQuotaDbRefreshIntervalStr] = useState( + String(initialSettings.quotaDbRefreshIntervalSeconds ?? 10) ); + const quotaDbRefreshIntervalSeconds = (() => { + const n = Number(quotaDbRefreshIntervalStr); + return Number.isFinite(n) ? n : 10; + })();在
onChange中直接存字符串,在onBlur中做 clamp:onChange={(e) => { - const raw = e.target.value; - if (!raw) { - setQuotaDbRefreshIntervalSeconds(1); - return; - } - const parsed = Number(raw); - if (!Number.isFinite(parsed)) return; - const clamped = Math.min(300, Math.max(1, parsed)); - setQuotaDbRefreshIntervalSeconds(clamped); + setQuotaDbRefreshIntervalStr(e.target.value); }} + onBlur={() => { + const parsed = Number(quotaDbRefreshIntervalStr); + if (!Number.isFinite(parsed) || parsed < 1) { + setQuotaDbRefreshIntervalStr("1"); + } else if (parsed > 300) { + setQuotaDbRefreshIntervalStr("300"); + } + }}
| onChange={(e) => { | ||
| const raw = e.target.value; | ||
| if (!raw) { | ||
| setQuotaDbRefreshIntervalSeconds(1); | ||
| return; | ||
| } | ||
| const parsed = Number(raw); | ||
| if (!Number.isFinite(parsed)) return; | ||
| const clamped = Math.min(300, Math.max(1, parsed)); | ||
| setQuotaDbRefreshIntervalSeconds(clamped); | ||
| }} |
There was a problem hiding this comment.
Clearing input forces 1
In the quota-db-refresh-interval controlled <Input type="number">, onChange sets the state to 1 whenever the user clears the field (raw === ""). This makes it very hard to edit multi-digit values by backspacing (the field snaps to 1 immediately), and it prevents temporarily emptying the input while typing. Consider allowing an empty state (e.g., store "" separately) or keeping the previous value when raw is empty instead of forcing 1.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/[locale]/settings/config/_components/system-settings-form.tsx
Line: 631:641
Comment:
**Clearing input forces `1`**
In the `quota-db-refresh-interval` controlled `<Input type="number">`, `onChange` sets the state to `1` whenever the user clears the field (`raw === ""`). This makes it very hard to edit multi-digit values by backspacing (the field snaps to `1` immediately), and it prevents temporarily emptying the input while typing. Consider allowing an empty state (e.g., store `""` separately) or keeping the previous value when `raw` is empty instead of forcing `1`.
How can I resolve this? If you propose a fix, please make it concise.|
已按 CodeRabbit 的关键建议做了修正(均为最小改动,且仅影响本 PR 相关代码):
对应提交: |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/app/[locale]/settings/config/_components/system-settings-form.tsx (1)
640-657:⚠️ Potential issue | 🟡 Minor刷新间隔输入允许非整数值,后端验证会拒绝。
<Input>的type="number"没有设置step={1},用户可以输入1.5等小数值。onBlur的 clamp 逻辑(第 647-652 行)仅限制范围 [1, 300],但不执行取整。当提交表单时,后端验证(src/lib/validation/schemas.ts中的.int())会拒绝非整数值,返回错误 "DB refresh interval must be an integer"。建议添加
step={1}属性到<Input>组件,或在 clamp 时使用Math.round()转换为整数。
🧹 Nitpick comments (1)
src/app/[locale]/settings/config/_components/system-settings-form.tsx (1)
148-153:onBlur与handleSubmit中的 clamp 逻辑重复。Lines 148-153 的保存时 clamp 和 Lines 647-654 的
onBlurclamp 逻辑几乎完全一致,建议提取为共享的辅助函数(如clampRefreshInterval),减少维护时两处不一致的风险。提取共享 clamp 函数
+const clampRefreshInterval = (raw: string): number => { + const parsed = Number(raw); + if (!Number.isFinite(parsed) || parsed < 1) return 1; + if (parsed > 300) return 300; + return parsed; +}; + const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); // ... - const quotaDbRefreshIntervalSecondsToSave = (() => { - const parsed = Number(quotaDbRefreshIntervalSecondsStr); - if (!Number.isFinite(parsed) || parsed < 1) return 1; - if (parsed > 300) return 300; - return parsed; - })(); + const quotaDbRefreshIntervalSecondsToSave = clampRefreshInterval(quotaDbRefreshIntervalSecondsStr);
onBlur同样可复用:onBlur={() => { - const parsed = Number(quotaDbRefreshIntervalSecondsStr); - if (!Number.isFinite(parsed) || parsed < 1) { - setQuotaDbRefreshIntervalSecondsStr("1"); - } else if (parsed > 300) { - setQuotaDbRefreshIntervalSecondsStr("300"); - } + setQuotaDbRefreshIntervalSecondsStr( + String(clampRefreshInterval(quotaDbRefreshIntervalSecondsStr)) + ); }}
Additional Comments (1)
Prompt To Fix With AIThis is a comment left during a code review.
Path: src/lib/provider-endpoints/probe.ts
Line: 211:216
Comment:
**Ignores configured HTTP probe method**
`probeEndpointUrl` always probes with `HEAD` then falls back to `GET` (`src/lib/provider-endpoints/probe.ts:211-216`), but `resolveProbeMethod()` is intended to let `ENDPOINT_PROBE_METHOD` choose between `HEAD` and `GET`. With `ENDPOINT_PROBE_METHOD=GET`, the function still sends `HEAD` requests, which is a behavior bug and can break probing against endpoints that disallow `HEAD`.
```suggestion
// HTTP-based probing: try configured method first, optionally fallback
if (method === "GET") {
return tryProbe(url, "GET", timeoutMs);
}
const head = await tryProbe(url, "HEAD", timeoutMs);
if (head.statusCode === null) {
return tryProbe(url, "GET", timeoutMs);
}
return head;
```
How can I resolve this? If you propose a fix, please make it concise. |
|
补充跟进 CodeRabbit 最新评论(均为最小改动,限定在本 PR 范围内):
对应提交: |
| onBlur={() => { | ||
| setQuotaDbRefreshIntervalSecondsStr( | ||
| String(clampQuotaDbRefreshIntervalSeconds(quotaDbRefreshIntervalSecondsStr)) | ||
| ); |
There was a problem hiding this comment.
Empty blur clamps to 1
onBlur always normalizes the input via clampQuotaDbRefreshIntervalSeconds(quotaDbRefreshIntervalSecondsStr), but clampQuotaDbRefreshIntervalSeconds uses Number(raw). In JS, Number("") === 0, so if the user clears the field and blurs, the value will be forced to 1 again. This recreates the prior UX bug where clearing the refresh interval snaps to 1 (it will happen on blur now).
A simple fix is to treat empty/whitespace as “leave empty” (don’t clamp) or bot keep the previous value when quotaDbRefreshIntervalSecondsStr.trim() is empty.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/[locale]/settings/config/_components/system-settings-form.tsx
Line: 652:655
Comment:
**Empty blur clamps to 1**
`onBlur` always normalizes the input via `clampQuotaDbRefreshIntervalSeconds(quotaDbRefreshIntervalSecondsStr)`, but `clampQuotaDbRefreshIntervalSeconds` uses `Number(raw)`. In JS, `Number("") === 0`, so if the user clears the field and blurs, the value will be forced to `1` again. This recreates the prior UX bug where clearing the refresh interval snaps to `1` (it will happen on blur now).
A simple fix is to treat empty/whitespace as “leave empty” (don’t clamp) or bot keep the previous value when `quotaDbRefreshIntervalSecondsStr.trim()` is empty.
How can I resolve this? If you propose a fix, please make it concise.|
看到了 Greptile 关于 不过该问题不在本 PR 的新增“表单 warning/校验提示”范围内,也不是本 PR 新增的代码。为避免扩大 PR 范围,这里先不混在一起改;我会另起一个 follow-up PR 专门修复 |
* fix(circuit-breaker): key errors should not trip endpoint circuit breaker Remove 3 recordEndpointFailure calls from response-handler streaming error paths (fake-200, non-200 HTTP, stream abort). These are key-level errors where the endpoint itself responded successfully. Only forwarder-level failures (timeout, network error) and probe failures should penalize the endpoint circuit breaker. Previously, a single bad API key could trip the endpoint breaker (threshold=3, open=5min), making ALL keys on that endpoint unavailable. * chore: format code (dev-3d584e5) * Merge pull request #767 from ding113/fix/provider-clone-deep-copy fix: 修复供应商克隆时因浅拷贝引用共享导致源供应商数据被意外污染的问题 * 增强配置表单输入警告提示 (#768) * feat: 增强配置表单输入警告提示 * fix: 修复 expiresAt 显示与配额刷新输入边界 * fix: 修复 expiresAt 解析兜底并改善刷新间隔输入体验 * fix: 刷新间隔输入取整并复用 clamp --------- Co-authored-by: tesgth032 <tesgth032@users.noreply.github.com> * feat(circuit-breaker): endpoint CB default-off + 524 decision chain audit (#773) * feat(circuit-breaker): endpoint circuit breaker default-off + 524 decision chain audit - Add ENABLE_ENDPOINT_CIRCUIT_BREAKER env var (default: false) to gate endpoint-level circuit breaker - Gate isEndpointCircuitOpen, recordEndpointFailure, recordEndpointSuccess, triggerEndpointCircuitBreakerAlert behind env switch - Add initEndpointCircuitBreaker() startup cleanup: clear stale Redis keys when feature disabled - Gate endpoint filtering in endpoint-selector (getPreferredProviderEndpoints, getEndpointFilterStats) - Fix 524 vendor-type timeout missing from decision chain: add chain entry with reason=vendor_type_all_timeout in forwarder - Add vendor_type_all_timeout to ProviderChainItem reason union type (both backend session.ts and frontend message.ts) - Add timeline rendering for vendor_type_all_timeout in provider-chain-formatter - Replace hardcoded Chinese strings in provider-selector circuit_open details with i18n keys - Add i18n translations for vendor_type_all_timeout and filterDetails (5 languages: zh-CN, zh-TW, en, ja, ru) - Enhance LogicTraceTab to render filterDetails via i18n lookup with fallback - Add endpoint_pool_exhausted and vendor_type_all_timeout to provider-chain-popover isActualRequest/getItemStatus - Add comprehensive unit tests for all changes (endpoint-circuit-breaker, endpoint-selector, provider-chain-formatter) * fix(i18n): fix Russian grammar errors and rate_limited translations - Fix Russian: "конечная точкаов" -> "конечных точек" (11 occurrences) - Fix Russian: "Ограничение стоимости" -> "Ограничение скорости" (rate_limited) - Fix zh-CN: "费用限制" -> "速率限制" (filterDetails.rate_limited) - Fix zh-TW: "費用限制" -> "速率限制" (filterDetails.rate_limited) - Add initEndpointCircuitBreaker() to dev environment in instrumentation.ts * fix(circuit-breaker): vendor type CB respects ENABLE_ENDPOINT_CIRCUIT_BREAKER Make vendor type circuit breaker controlled by the same ENABLE_ENDPOINT_CIRCUIT_BREAKER switch as endpoint circuit breaker. When disabled (default), vendor type CB will never trip or block providers, resolving user confusion about "vendor type temporary circuit breaker" skip reasons in decision chain. Changes: - Add ENABLE_ENDPOINT_CIRCUIT_BREAKER check in isVendorTypeCircuitOpen() - Add switch check in recordVendorTypeAllEndpointsTimeout() - Add tests for switch on/off behavior Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * 修复 Key 并发限制继承用户并发上限 (#772) * fix: Key 并发上限默认继承用户限制 - RateLimitGuard:Key limitConcurrentSessions=0 时回退到 User limitConcurrentSessions\n- Key 配额/使用量接口:并发上限按有效值展示\n- 单测覆盖并发继承逻辑;补齐 probe 测试的 endpoint-circuit-breaker mock 导出\n- 同步更新 biome.json schema 版本以匹配当前 Biome CLI * docs: 补齐并发上限解析工具注释 * refactor: 合并 Key 限额查询并补充并发单测 - getKeyQuotaUsage/getKeyLimitUsage:通过 leftJoin 一次取回 User 并发上限,避免额外查询\n- 新增 resolveKeyConcurrentSessionLimit 单测,覆盖关键分支\n- 修复 vacuum-filter bench 中的 Biome 报错 * fix: my-usage 并发上限继承用户限制 - getMyQuota:Key 并发为 0/null 时回退到 User 并发上限,保持与 Guard/Key 配额一致\n- 新增单测覆盖 Key->User 并发继承 * test: 补齐 my-usage 并发继承场景 - MyUsageQuota.keyLimitConcurrentSessions 收敛为 number(0 表示无限制)\n- OpenAPI 响应 schema 同步为非 nullable\n- my-usage 并发继承测试补充 Key>0 与 User=0 场景 --------- Co-authored-by: tesgth032 <tesgth032@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: hank9999 <hank9999@qq.com> Co-authored-by: tesgth032 <tesgth032@hotmail.com> Co-authored-by: tesgth032 <tesgth032@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Summary
Add non-blocking inline warnings across three configuration forms (provider settings, system settings, user management) to help admins catch common input mistakes before saving. No business logic changes — warnings are advisory only.
Problem
Several configuration fields (API keys, quota lease parameters, user expiration dates) are prone to accidental mis-paste or mis-entry that can be hard to notice. For example:
Solution
Add yellow inline warning hints (non-blocking, save is never prevented) using a new shared
InlineWarningUI component. Warnings appear contextually below the relevant input fields.Changes
Core Changes
src/lib/utils/validation/api-key-warnings.ts): Detects authorization headers, surrounding quotes, non-ASCII chars, whitespace, and uncommon ASCII symbols. Skips whitespace/symbol checks for JSON credentials.src/lib/utils/validation/quota-lease-warnings.ts): Warns on zero lease percent and zero lease cap.src/app/[locale]/settings/config/_components/system-settings-form.tsx): Adds DB refresh interval warnings (too low <=2s, too high >=60s) and lease parameter warnings.src/app/[locale]/settings/providers/_components/forms/provider-form/sections/basic-info-section.tsx): Integrates API key warnings below the key input.src/app/[locale]/dashboard/_components/user/forms/user-form.tsx): Adds past-date warning forexpiresAtfield, plus a date parsing fix (src/lib/utils/date-input.ts) to use local timezone end-of-day instead of UTC midnight.Supporting Changes
InlineWarningcomponent (src/components/ui/inline-warning.tsx): Shared yellow warning display with AlertTriangle icon.api-key-warnings,quota-lease-warnings, anddate-inpututilities.Breaking Changes
None. This PR only adds advisory warnings — no existing behavior is modified.
Testing
Automated Tests
api-key-warnings.test.ts,quota-lease-warnings.test.ts,date-input.test.tsManual Testing
Beareror surrounding quotes — yellow warning should appearChecklist
Original description (Chinese)
背景
供应商配置/系统配置里有些字段(如 API Key、配额租约参数等)在误粘贴/误填写时很难被及时发现。本 PR 仅新增"警告提示(warning)",不阻止保存、不改变原有业务逻辑。
变更点
1) 供应商设置:API Key 异常输入提示(仅警告)
{开头),不提示"空白/不常见符号",避免误报。2) 系统设置:配额租约/刷新频率提示(仅警告)
3) 用户管理:expiresAt 过去时间提示(仅警告)+ 纯日期解析修复
YYYY-MM-DD -> 本地当天 23:59:59.999的解析函数,避免new Date("YYYY-MM-DD")的 UTC 解析导致提前过期。4) UI 组件统一
InlineWarning组件,统一 warning 的展示样式(黄色小字 + AlertTriangle 图标)。测试
npm run lintnpm run typechecknpm run testnode_modules为 symlink,next build(Turbopack)会报错;已使用npx next build --webpack验证构建通过,并执行node scripts/copy-version-to-standalone.cjs。Description enhanced by Claude AI
Greptile Overview
Greptile Summary
This PR adds non-blocking, inline warning hints across configuration forms to help admins catch common input mistakes.
Key changes:
InlineWarningUI component and integrates it into provider key input, system quota/refresh interval inputs, and userexpiresAtdate input.detectApiKeyWarnings, quota lease/refresh warning predicates) and date-only utilities (formatDateToLocalYmd,parseYmdToLocalEndOfDay) to avoid UTC date parsing pitfalls.One functional regression remains in the system settings DB refresh interval field: clearing the numeric input and blurring will still normalize back to
1due to how the clamp function parses empty string, reintroducing the previously-raised editing UX issue.Confidence Score: 3/5
1on blur after clearing due toNumber("")clamping behavior, undermining the intended improved editing experience.Important Files Changed
Sequence Diagram