Skip to content

feat: polish settings, reset workflow, Doctor, usage, and proxy streaming#180

Merged
ymkiux merged 42 commits into
mainfrom
feat/settings-language-selector
May 30, 2026
Merged

feat: polish settings, reset workflow, Doctor, usage, and proxy streaming#180
ymkiux merged 42 commits into
mainfrom
feat/settings-language-selector

Conversation

@awsl233777
Copy link
Copy Markdown
Collaborator

@awsl233777 awsl233777 commented May 29, 2026

Summary

  • Add the Settings language selector UI and zh/en/ja i18n coverage for the new visible labels.
  • Polish the OpenClaw config panel layout without adding/removing elements: constrain the panel width, let existing cards fill their section cleanly, keep mobile config status visible, and fill missing side-rail i18n labels.
  • Add a safer npm run reset target-selection flow: explicit PR numbers still reset directly to that PR snapshot, while no-arg interactive reset asks for a target where Enter selects origin/main and a numeric input selects the matching PR.
  • Refuse no-arg reset in non-interactive mode so scripts cannot silently run the destructive main reset path.
  • Harden the OpenAI-compatible Responses ↔ Chat fallback used by the Codex bridge and builtin proxy so tool-call semantics survive fallback, including explicit function tools named apply_patch.
  • Preserve Codex Responses tool call item types when Chat fallback returns tool_calls: custom_tool_call, local_shell_call, and ordinary function_call no longer collapse into one generic shape.
  • Keep long-running Codex command sessions alive through Chat fallback history conversion and polling.
  • Keep Doctor/health report export actions available for clean reports and guard navigate actions without targets.

Validation

  • NODE_PATH=/home/moltbot/clawd-wechat/codexmate/node_modules npm run test:unitAll 563 tests passed.
  • NODE_PATH=/home/moltbot/clawd-wechat/codexmate/node_modules npm run lintLint passed for 204 file(s).
  • NODE_PATH=/home/moltbot/clawd-wechat/codexmate/node_modules npm run test:e2e → passed.
  • Browser visual QA for OpenClaw tab at 1440px / 900px / 500px → no horizontal overflow, no clipped controls, no raw i18n keys; mobile keeps the existing 已配置 status visible.
  • Final OpenClaw screenshots captured after rebuilding web-ui/res/web-ui-render.precompiled.js.
  • NODE_PATH=/home/moltbot/clawd-wechat/codexmate/node_modules node tools/dev/reset-main.js with no args in non-interactive mode → exits 1 before reset/clean and prints the reset-target-required error.

Notes

  • No API keys are included in the validation logs or PR text.
  • Remaining merge gate is repository review policy (REVIEW_REQUIRED) if checks stay green.

Summary by CodeRabbit

  • New Features

    • Single language settings control (Vietnamese added) with easy access; OpenClaw quick setup converted to an accordion stepper with validation; toggleable API-key visibility in provider/Claude modals.
  • Improvements

    • Wide UI localization (webhooks, trash, plugins, templates, tooltips, settings), accessibility/focus behavior for language settings, responsive/layout refinements (dashboard, OpenClaw, mobile), and more robust Responses↔Chat streaming and conversion.
  • Tests

    • Expanded unit tests for Responses shim, OpenAI bridge, UI parity, and release changelog tooling.
  • Chores

    • Version bumped to 0.0.39; release changelog generator and interactive reset tooling added.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

Warning

Review limit reached

@awsl233777, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 17 minutes and 20 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dabecbbb-a650-4b52-b4af-cc57b095141a

📥 Commits

Reviewing files that changed from the base of the PR and between 188dd35 and dd8c210.

📒 Files selected for processing (3)
  • web-ui/modules/i18n.dict.mjs
  • web-ui/partials/index/panel-config-openclaw.html
  • web-ui/res/web-ui-render.precompiled.js
📝 Walkthrough

Walkthrough

Adds metadata-driven i18n and a single language-settings UI, internationalizes templates, introduces an OpenClaw accordion editor with validation, overhauls Responses↔Chat normalization and SSE streaming, adds release-changelog tooling and interactive reset CLI, plus tests, CSS, and a package version bump.

Changes

Monolithic PR: i18n, OpenClaw, Proxy, Tests & Tools

Layer / File(s) Summary
i18n core and API
web-ui/modules/i18n.mjs, web-ui/modules/i18n.dict.mjs, web-ui/partials/index/layout-header.html, web-ui/res/web-ui-render.precompiled.js
Adds LANGUAGE_META, metadata-driven normalizeLang, applyDocumentLanguage, and exposes languageOptions(), currentLanguageLabel(), openLanguageSettings(); header/sidebar language UI wired into templates and render output.
Template localization & API-key toggles
web-ui/partials/*, web-ui/res/web-ui-render.precompiled.js, tests/unit/web-ui-behavior-parity.test.mjs
Internationalizes webhook, trash, provider/Claude modals, changes API-key inputs to input-with-toggle bound to show*Key flags, updates parity tests to allow new data/method keys.
Language styling & responsive
web-ui/styles/layout-shell.css, web-ui/styles/settings-panel.css, web-ui/styles/responsive.css
Add .language-settings-link pill styles, contextual overrides, small-screen select sizing, and a new mobile breakpoint collapsing layout to single-column.
Vue state & methods (key toggles, OpenClaw)
web-ui/app.js, web-ui/modules/app.methods.claude-config.mjs, web-ui/modules/app.methods.providers.mjs, web-ui/modules/app.methods.openclaw-editing.mjs
Add showAddProviderKey/showAddClaudeConfigKey, OpenClaw openclawAccordionStep and openclawValidation state, toggle/reset methods for key visibility, and OpenClaw accordion navigation/validation methods.
OpenClaw UI & styles
web-ui/partials/index/modal-openclaw-config.html, web-ui/partials/index/panel-config-openclaw.html, web-ui/styles/openclaw-structured.css, web-ui/styles/controls-forms.css
Refactor quick-steps into an accordion stepper with gated navigation and validation; restructure workspace/tools UI; add .btn-sm and accordion styles.
Responses↔Chat proxy overhaul
cli/builtin-proxy.js
Add robust normalization for Responses→Chat, aggregate/prune tool-calls and outputs, propagate toolTypesByName, rework SSE streaming init/state/retries/timeout/abort handling, and route streaming shim through Chat fallback with tool metadata.
OpenAI bridge conversion updates
cli/openai-bridge.js
New JSON/tool/content normalization helpers, inject Codex exec fallback runtime guidance, prune invalid tool_choice, and round-trip tool types via added options.toolTypesByName parameter.
Tests: proxy, bridge, UI parity
tests/unit/*
Add/modify tests for Responses→chat conversions, streaming recovery and idle/retry scenarios, preserve/prune tool types, update UI parity tests to allow new data/method keys and check key-visibility toggle bindings.
Release changelog tooling & CI
tools/release/changelog.js, .github/workflows/release.yml, tests/unit/release-changelog.test.mjs, tests/unit/run.mjs
Add CLI to compute git-based changelog, unit tests, and CI step to print changelog conditionally.
Interactive reset CLI
tools/dev/reset-main.js, tests/unit/reset-main.test.mjs
Make reset interactive by default, add readline-based prompting and exported helpers for argument/TTY detection and resolution; update tests.
Misc: CSS, package, pnpm
web-ui/styles/responsive.css, web-ui/styles/sessions-usage.css, web-ui/styles/dashboard.css, web-ui/styles/layout-shell.css, web-ui/styles/openclaw-structured.css, web-ui/styles/navigation-panels.css, web-ui/styles/controls-forms.css, package.json, pnpm-workspace.yaml
Add responsive breakpoint and layout tweaks, heatmap responsiveness, dashboard alignment updates, pill/button utilities, bump package version 0.0.38→0.0.39, and add pnpm allowBuilds esbuild flag.

Estimated code review effort:
🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs:

Suggested reviewers:

  • ymkiux

"A rabbit hops through code and keys,
name strings turned to i18n breeze.
Accordions click, the proxy hums,
changelogs print and tests come.
🐇 — cheers to the new release!"

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/settings-language-selector

Copy link
Copy Markdown
Contributor

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

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

Inline comments:
In `@web-ui/res/web-ui-render.precompiled.js`:
- Around line 132-137: The button's aria-label
(_ctx.t('settings.language.open')) overrides the visible text and hides the
current language from screen readers; change the aria-label to use the same
translated label as the visible text (use _ctx.t('settings.language.sideLabel',
{ language: _ctx.currentLanguageLabel() }) ), or remove the explicit aria-label
altogether so the accessible name comes from the button text; apply the same
change to the other identical occurrence that uses class
"language-settings-link" and onClick _ctx.openLanguageSettings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 276a0a69-4196-4984-a84b-f33cb600a8fc

📥 Commits

Reviewing files that changed from the base of the PR and between 5769ed4 and 789a08e.

📒 Files selected for processing (9)
  • tests/unit/web-ui-behavior-parity.test.mjs
  • web-ui/modules/i18n.dict.mjs
  • web-ui/modules/i18n.mjs
  • web-ui/partials/index/layout-header.html
  • web-ui/partials/index/modal-webhook.html
  • web-ui/partials/index/panel-settings.html
  • web-ui/res/web-ui-render.precompiled.js
  • web-ui/styles/layout-shell.css
  • web-ui/styles/settings-panel.css
📜 Review details
🧰 Additional context used
🪛 HTMLHint (1.9.2)
web-ui/partials/index/modal-webhook.html

[error] 2-2: Doctype must be declared before any non-comment content.

(doctype-first)

🔇 Additional comments (9)
web-ui/modules/i18n.mjs (1)

5-33: LGTM!

Also applies to: 52-59, 60-84, 93-93

web-ui/modules/i18n.dict.mjs (1)

157-158: LGTM!

Also applies to: 916-980, 1249-1250, 1996-2060, 2330-2331, 3090-3150

tests/unit/web-ui-behavior-parity.test.mjs (1)

620-623: LGTM!

web-ui/partials/index/layout-header.html (1)

96-101: LGTM!

Also applies to: 270-270, 296-304, 309-314

web-ui/res/web-ui-render.precompiled.js (1)

366-366: LGTM!

Also applies to: 397-399, 415-415, 3975-4004, 4055-4083, 4165-4165, 5849-5918

web-ui/partials/index/panel-settings.html (1)

43-63: LGTM!

Also applies to: 100-114, 160-160

web-ui/partials/index/modal-webhook.html (1)

1-1: LGTM!

Also applies to: 4-4, 7-7, 10-10, 15-15, 26-26, 36-36, 38-38

web-ui/styles/layout-shell.css (1)

668-711: LGTM!

web-ui/styles/settings-panel.css (1)

417-419: LGTM!

Comment thread web-ui/res/web-ui-render.precompiled.js
@awsl233777
Copy link
Copy Markdown
Collaborator Author

Updated this PR for v0.0.39.

Changes in the latest commit:

  • bumped package metadata to 0.0.39
  • pushed remote tag v0.0.39 to aa44fa53640eb772f2d58d6db13980c51eb0d6f9
  • localized the remaining visible Settings/Webhook/Trash/provider/toast labels found during the hardcoded i18n pass
  • refreshed the precompiled Web UI render bundle

Validation actually run:

  • npm run lint
  • npm run test:unit (535 tests passed)
  • npm run test:e2e
  • git diff --check
  • English visible-text scan; remaining Chinese samples are historical session/task content, not UI strings

@coderabbitai re-review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

@awsl233777 Thanks for the detailed update! I'll kick off a fresh review of the PR with the latest v0.0.39 changes now.

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

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

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

Inline comments:
In `@web-ui/modules/app.methods.runtime.mjs`:
- Around line 64-68: translateUiMessage currently relies on exact matches in
UI_MESSAGE_KEY_BY_TEXT so dynamic/prefixed toast texts like "导入成功..." or
"备份失败:..." are not localized; update translateUiMessage to detect and map such
cases by iterating UI_MESSAGE_KEY_BY_TEXT keys and finding the best prefix or
regex match (prefer longest matching key) against the incoming text, then call
context.t(foundKey) if a match is found, otherwise fall back to returning the
original text; reference translateUiMessage, UI_MESSAGE_KEY_BY_TEXT, and
context.t when locating where to implement this change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 559b1a50-7314-4539-bca7-e2ea3647723c

📥 Commits

Reviewing files that changed from the base of the PR and between 789a08e and aa44fa5.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • package.json
  • web-ui/modules/app.computed.main-tabs.mjs
  • web-ui/modules/app.methods.providers.mjs
  • web-ui/modules/app.methods.runtime.mjs
  • web-ui/modules/config-mode.computed.mjs
  • web-ui/modules/i18n.dict.mjs
  • web-ui/partials/index/modals-basic.html
  • web-ui/partials/index/panel-config-codex.html
  • web-ui/partials/index/panel-trash.html
  • web-ui/res/web-ui-render.precompiled.js
✅ Files skipped from review due to trivial changes (3)
  • web-ui/modules/app.methods.providers.mjs
  • package.json
  • web-ui/res/web-ui-render.precompiled.js
📜 Review details
🧰 Additional context used
🪛 HTMLHint (1.9.2)
web-ui/partials/index/panel-trash.html

[error] 2-2: Doctype must be declared before any non-comment content.

(doctype-first)

web-ui/partials/index/panel-config-codex.html

[error] 129-129: Special characters must be escaped : [ > ].

(spec-char-escape)

🔇 Additional comments (6)
web-ui/partials/index/panel-trash.html (1)

18-18: LGTM!

Also applies to: 34-34, 41-42, 49-49, 66-67, 69-70, 77-77

web-ui/partials/index/modals-basic.html (1)

175-175: LGTM!

Also applies to: 180-183, 185-186, 190-191

web-ui/modules/app.computed.main-tabs.mjs (1)

46-48: LGTM!

Also applies to: 50-84, 88-137, 151-152, 163-164, 175-176, 206-207, 209-210

web-ui/modules/config-mode.computed.mjs (1)

38-39: LGTM!

Also applies to: 41-42, 47-48

web-ui/modules/i18n.dict.mjs (1)

61-65: LGTM!

Also applies to: 70-123, 231-231, 774-780, 912-941, 1048-1054, 1063-1063, 1254-1258, 1263-1316, 1424-1424, 1955-1960, 2093-2122, 2229-2235, 2244-2244, 2436-2440, 2445-2498, 2606-2606, 3150-3155, 3288-3317, 3420-3427, 3435-3435

web-ui/partials/index/panel-config-codex.html (1)

125-125: LGTM!

Also applies to: 129-129, 143-143

Comment thread web-ui/modules/app.methods.runtime.mjs
@awsl233777
Copy link
Copy Markdown
Collaborator Author

Fixed the CodeRabbit actionable comment in a follow-up commit.

Latest commit:

  • 7249ba49021c9909a2070e65183ec13430e34c74

What changed:

  • translateUiMessage now uses exact-match translation first, then longest-prefix fallback for dynamic toast messages
  • added toast.importSuccess translations for zh/ja/en
  • preserved dynamic suffix details after the translated prefix, e.g. import/backup messages with extra context
  • moved remote tag v0.0.39 to the latest commit

Validation actually run after the fix:

  • npm run lint
  • npm run test:unit (535 tests passed)
  • npm run test:e2e
  • git diff --check

@coderabbitai re-review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

@awsl233777 Thanks for addressing the actionable comment! The exact-match + longest-prefix fallback strategy for translateUiMessage sounds like a solid approach for handling dynamic toast messages while preserving suffix context. Kicking off a fresh review of the latest commit now.

(ᵔᴥᵔ)🐇

✅ Actions performed

Full review triggered.

@awsl233777 awsl233777 changed the title feat(web-ui): move language selector into settings feat(web-ui): move language selector to settings and bump v0.0.39 May 29, 2026
@awsl233777 awsl233777 force-pushed the feat/settings-language-selector branch from 7249ba4 to 97370b5 Compare May 29, 2026 10:55
@awsl233777 awsl233777 changed the title feat(web-ui): move language selector to settings and bump v0.0.39 feat(web-ui): move language selector and harden proxy streaming May 29, 2026
@awsl233777
Copy link
Copy Markdown
Collaborator Author

Added a follow-up fix for the built-in Codex proxy streaming path.

Latest commit:

  • 381dc38 fix(proxy): retry pre-output streaming aborts

What changed:

  • delayed client-visible Responses SSE startup for chat-completions streaming fallback until the first actual output/completion
  • retries transient upstream aborted / stream-abort failures before any client-visible output is emitted
  • preserves existing mid-flight behavior after output starts: emit response.failed instead of retrying, so duplicated tokens/events are avoided
  • added a regression test covering pre-output stream abort recovery without response.failed

Validation actually run:

  • node --test tests/unit/builtin-proxy-responses-shim.test.mjs — 9/9 passed
  • npm run test:unit — All 536 tests passed
  • npm run lint — 202 files passed
  • git diff --check

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

@awsl233777 Thanks for the detailed summary! The pre-output retry logic with the mid-flight safeguard (emitting response.failed instead of retrying after output has started) sounds like a solid approach. I'll kick off a fresh review of the latest commit now.

(ᵔᴥᵔ)🐇

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@awsl233777 awsl233777 force-pushed the feat/settings-language-selector branch from 381dc38 to c35d11e Compare May 29, 2026 12:13
@awsl233777 awsl233777 changed the title feat(web-ui): move language selector and harden proxy streaming feat(web-ui): move language selector, harden proxy streaming, and bump v0.0.39 May 29, 2026
Localize remaining Settings/Webhook/Trash/provider/toast UI labels, keep dynamic toast translation fallback behavior, refresh the precompiled Web UI render bundle, bump package metadata to 0.0.39, and retry built-in proxy streaming aborts before any client-visible SSE output.
@awsl233777 awsl233777 force-pushed the feat/settings-language-selector branch from c35d11e to ab4d7cd Compare May 29, 2026 12:20
@awsl233777
Copy link
Copy Markdown
Collaborator Author

Updated the built-in Codex proxy stream hardening after observing long-running Codex work still stopping around the short request timeout window.

Latest head:

  • ab4d7cd chore(web-ui): localize settings and harden proxy streaming

Additional fix:

  • accepted upstream text/event-stream responses now use a longer stream idle timeout instead of the short JSON request timeout
  • the proxy starts client SSE/heartbeat on the first valid upstream SSE chunk, including role-only chunks, so long Codex work does not leave the client with a silent connection
  • pre-output upstream abort retry remains covered
  • mid-flight failures after output starts still emit response.failed instead of retrying, avoiding duplicated tokens/events

Validation actually run:

  • node --test tests/unit/builtin-proxy-responses-shim.test.mjs — 10/10 passed
  • npm run test:unit — All 537 tests passed
  • npm run lint — 202 files passed
  • git diff --check

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

@awsl233777 Thanks for the thorough update! The combination of a longer stream idle timeout for text/event-stream responses and starting the client SSE/heartbeat on the first valid upstream chunk (including role-only chunks) sounds like the right fix for long-running Codex work silently timing out. The retained pre-output retry + mid-flight response.failed guard should keep the behavior safe against duplicate tokens. Kicking off a fresh review of the latest commit now.

(ᵔᴥᵔ)🐇

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

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

Caution

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

⚠️ Outside diff range comments (1)
cli/builtin-proxy.js (1)

946-967: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Delay SSE commitment in the non-stream JSON fallback path too.

This branch still sends 200 text/event-stream before the upstream body completes. If that JSON response aborts mid-transfer, handleAbort() sees res.headersSent and turns it into a visible response.failed instead of returning { ok: false } for the transient retry path. That leaves one pre-output abort case un-hardened.

Suggested fix
                 if (!/text\/event-stream/i.test(contentType)) {
-                    res.writeHead(200, {
-                        'Content-Type': 'text/event-stream; charset=utf-8',
-                        'Cache-Control': 'no-cache',
-                        'Connection': 'keep-alive',
-                        'X-Accel-Buffering': 'no'
-                    });
                     upstreamRes.on('data', (chunk) => chunk && chunks.push(chunk));
                     upstreamRes.on('end', () => {
                         const text = chunks.length ? Buffer.concat(chunks).toString('utf-8') : '';
                         const parsedJson = parseJsonOrError(text);
+                        res.writeHead(200, {
+                            'Content-Type': 'text/event-stream; charset=utf-8',
+                            'Cache-Control': 'no-cache',
+                            'Connection': 'keep-alive',
+                            'X-Accel-Buffering': 'no'
+                        });
                         if (parsedJson.error) {
                             writeSse(res, 'response.failed', { type: 'response.failed', error: `invalid upstream response: ${parsedJson.error}` });
                             writeSse(res, 'done', '[DONE]');
                             res.end();
                             finish({ ok: true });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli/builtin-proxy.js` around lines 946 - 967, The branch that handles
non-stream JSON currently calls res.writeHead('Content-Type':
'text/event-stream'...) immediately, causing headersSent to be true before
upstreamRes completes; move the res.writeHead call(s) into the
upstreamRes.on('end') handler (and into the error path) so headers are only sent
after parsing via parseJsonOrError succeeds or fails, then call
writeSse/sendResponsesSse and res.end, and finally finish({ok:true}) or
finish({ok:false}) as appropriate; update references in this block
(res.writeHead, upstreamRes.on('end', ...), parseJsonOrError, writeSse,
sendResponsesSse, finish) so aborts before end do not see headersSent and
handleAbort can return {ok:false}.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web-ui/partials/index/panel-trash.html`:
- Around line 66-69: The restore and purge icon-only buttons (handlers
restoreSessionTrash and purgeSessionTrash, using
sessionTrashRestoring/sessionTrashPurging and getSessionTrashActionKey) lack
accessible names for screen readers; add an explicit aria-label (or :aria-label
bound) to each button that mirrors the computed title text (e.g. the same
conditional used in :title:
sessionTrashRestoring[getSessionTrashActionKey(item)] ?
t('settings.trash.restoring') : t('settings.trash.restore') for the restore
button and sessionTrashPurging[...] ? t('settings.trash.purging') :
t('settings.trash.purge') for the purge button) so assistive tech gets a
reliable label while keeping the existing title and disabled logic.

---

Outside diff comments:
In `@cli/builtin-proxy.js`:
- Around line 946-967: The branch that handles non-stream JSON currently calls
res.writeHead('Content-Type': 'text/event-stream'...) immediately, causing
headersSent to be true before upstreamRes completes; move the res.writeHead
call(s) into the upstreamRes.on('end') handler (and into the error path) so
headers are only sent after parsing via parseJsonOrError succeeds or fails, then
call writeSse/sendResponsesSse and res.end, and finally finish({ok:true}) or
finish({ok:false}) as appropriate; update references in this block
(res.writeHead, upstreamRes.on('end', ...), parseJsonOrError, writeSse,
sendResponsesSse, finish) so aborts before end do not see headersSent and
handleAbort can return {ok:false}.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1e739f65-bd45-42b3-86ce-f36326cdb1bd

📥 Commits

Reviewing files that changed from the base of the PR and between aa44fa5 and c35d11e.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • cli/builtin-proxy.js
  • package.json
  • tests/unit/builtin-proxy-responses-shim.test.mjs
  • web-ui/modules/app.computed.main-tabs.mjs
  • web-ui/modules/app.methods.providers.mjs
  • web-ui/modules/app.methods.runtime.mjs
  • web-ui/modules/config-mode.computed.mjs
  • web-ui/modules/i18n.dict.mjs
  • web-ui/partials/index/modals-basic.html
  • web-ui/partials/index/panel-config-codex.html
  • web-ui/partials/index/panel-trash.html
  • web-ui/res/web-ui-render.precompiled.js
✅ Files skipped from review due to trivial changes (1)
  • package.json
🚧 Files skipped from review as they are similar to previous changes (6)
  • web-ui/modules/app.methods.providers.mjs
  • web-ui/partials/index/modals-basic.html
  • web-ui/modules/app.methods.runtime.mjs
  • web-ui/modules/app.computed.main-tabs.mjs
  • web-ui/modules/i18n.dict.mjs
  • web-ui/res/web-ui-render.precompiled.js
📜 Review details
🧰 Additional context used
🪛 HTMLHint (1.9.2)
web-ui/partials/index/panel-config-codex.html

[error] 129-129: Special characters must be escaped : [ > ].

(spec-char-escape)

web-ui/partials/index/panel-trash.html

[error] 2-2: Doctype must be declared before any non-comment content.

(doctype-first)

Comment thread web-ui/partials/index/panel-trash.html Outdated
@awsl233777
Copy link
Copy Markdown
Collaborator Author

@coderabbitai Could you please do a focused high-severity pass on the current PR head?

Please list only:

  • any P0/P1 issues that must be fixed before merge
  • concrete improvement suggestions for the built-in Codex proxy streaming retry/timeout changes
  • any compatibility risks around SSE semantics, retry safety, duplicated events/tokens, request timeout behavior, or accessibility changes in the trash actions

If there are no P0/P1 blockers, please say that explicitly and separate lower-priority suggestions from merge blockers.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the proxy file for retry logic, timeout, and SSE state flags
cat cli/builtin-proxy.js

Length of output: 76198


🏁 Script executed:

#!/bin/bash
# Read the new proxy shim test
cat tests/unit/builtin-proxy-responses-shim.test.mjs

Length of output: 32804


🏁 Script executed:

#!/bin/bash
# Read the runtime methods for translateUiMessage
cat web-ui/modules/app.methods.runtime.mjs

Length of output: 16533


🏁 Script executed:

#!/bin/bash
# Read the i18n module for applyDocumentLanguage and new methods
cat web-ui/modules/i18n.mjs

Length of output: 4265



@awsl233777 Here is the focused high-severity pass you requested.


✅ No P0/P1 blockers

The PR is safe to merge. All critical invariants hold:

  • response.created is emitted exactly once per request: beginChatStreamResponsesSse guards on state.started; each retry in retryTransientRequest creates a fresh state object, preventing duplicates. The test assertion (sse.text.match(/event: response\.created/g) || []).length === 1 validates this directly.
  • No infinite retry loop: TRANSIENT_RETRY_DELAYS_MS = [200, 600] caps retries at 3 total attempts.
  • Mid-flight response.failed guard is correct: handleAbort branches on streamState.started — retries only when SSE hasn't started, emits response.failed after output begins.
  • No double response.failed: finishChatStreamResponsesSse / failChatStreamResponsesSse both check state.finished before acting.
  • Lazy SSE header emission is safe: The non-text/event-stream branch (JSON upstream) writes headers eagerly but uses a completely separate code path with no state object, so there's no conflict with beginChatStreamResponsesSse.
  • Toast translation correctness: exact-match first, then longest-prefix fallback (sorted descending by length) correctly handles 导入成功,原配置已备份到临时文件:... and 备份失败:未知错误 with the right translated prefix + raw suffix preserved.

P2 — Concrete improvement suggestions for proxy streaming

These are not merge blockers but are worth tracking.

1. Infinite reschedule for accepted-stream timeouts (resource leak)

Once streamAccepted = true, the req.setTimeout callback fires every idle period and re-arms itself indefinitely, so a truly stalled-but-accepted upstream stream is never torn down at the Node.js layer:

req.setTimeout(timeoutMs, () => {
    if (streamAccepted) {
        req.setTimeout(STREAM_IDLE_TIMEOUT_MS); // fires again → reschedules again → forever
        return;
    }
    // …destroy path is never reached
});

Suggested fix — cap the reschedule or honour a maximum idle count:

let idleExtensions = 0;
const MAX_IDLE_EXTENSIONS = 3; // ~30 min total after stream accepted
req.setTimeout(timeoutMs, () => {
    if (streamAccepted && idleExtensions < MAX_IDLE_EXTENSIONS) {
        idleExtensions += 1;
        req.setTimeout(STREAM_IDLE_TIMEOUT_MS);
        return;
    }
    try { req.destroy(new Error('stream idle timeout')); } catch (_) {}
    finish({ ok: false, error: 'stream idle timeout' });
});

2. Client disconnect doesn't abort the upstream request

The res.on('close', ...) handler only stops the heartbeat but does not destroy the upstream req. If the browser tab is closed mid-stream, the upstream Codex work keeps running (consuming tokens/resources) until OS TCP timeout (~several minutes with keep-alive).

Suggested addition in beginChatStreamResponsesSse:

res.on('close', () => {
    stopChatStreamHeartbeat(state);
    if (!state.finished) {
        try { /* ref to upstream req */ upstreamReq.destroy(); } catch (_) {}
    }
});

This requires threading req (the upstream http.ClientRequest) into state, which is a modest refactor but worth it for long Codex sessions.


P2 — i18n compatibility note

normalizeLang() has a hardcoded 'zh' fallback rather than deriving it from getAvailableLanguages()[0].code. This is harmless today since zh is always first in LANGUAGE_META with a dictionary present, but would silently break if the ordering changed:

// current
return getAvailableLanguages().some(...) ? normalized : 'zh';

// more robust
const available = getAvailableLanguages();
const defaultCode = available.length > 0 ? available[0].code : 'zh';
return available.some(...) ? normalized : defaultCode;

Accessibility — trash actions ✅

The trash panel title attributes on restore/purge buttons are now driven by t('settings.trash.restoring') / t('settings.trash.purging') etc. No regression; the localised titles are semantically equivalent to the previous hardcoded Chinese strings.

@awsl233777
Copy link
Copy Markdown
Collaborator Author

P2 follow-up fixed in d745dd2.

Changes:

  • capped accepted streaming fallback idle extensions so truly stuck upstream streams eventually emit response.failed + [DONE] instead of hanging forever;
  • destroyed the upstream chat-completions request when the downstream Responses SSE client disconnects;
  • delayed the JSON fallback SSE header until the upstream body is complete, so pre-output JSON fallback aborts remain retryable;
  • made language fallback derive from available languages instead of hard-coding zh;
  • regenerated the checked-in precompiled Web UI render script after the trash panel aria-label fix.

Validation actually run:

  • node --test tests/unit/builtin-proxy-responses-shim.test.mjs → 13/13 passed
  • npm run test:unit → 540/540 passed

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check idle extension cap in builtin-proxy.js
rg -n "idleExtensions\|MAX_IDLE\|idle.*extension\|extension.*idle\|STREAM_IDLE\|streamAccepted" cli/builtin-proxy.js -A2 -B2

echo "---CLIENT DISCONNECT DESTROY---"
# Check upstream destroy on client disconnect
rg -n "destroy\|upstreamReq\|res\.on.*close\|close.*destroy" cli/builtin-proxy.js -A3 -B1

echo "---LANGUAGE FALLBACK---"
# Check language fallback in i18n.mjs
rg -n "normalizeLang\|getAvailableLanguages\|fallback\|defaultCode" web-ui/modules/i18n.mjs -A3 -B1

Length of output: 828


🏁 Script executed:

#!/bin/bash
# Find and verify the proxy file exists
fd "builtin-proxy.js" --type f

echo "---"
# Check i18n.mjs
fd "i18n.mjs" web-ui --type f

echo "---PROXY IDLE SECTION---"
grep -n "STREAM_IDLE\|idleExtension\|MAX_IDLE\|streamAccepted\|setTimeout" cli/builtin-proxy.js | head -40

echo "---DESTROY ON CLOSE---"
grep -n "destroy\|\.on('close\|\.on(\"close" cli/builtin-proxy.js | head -30

echo "---LANGUAGE FALLBACK---"
grep -n "normalizeLang\|getAvailableLanguages\|defaultCode\|available\[0\]" web-ui/modules/i18n.mjs | head -20

Length of output: 3124



@awsl233777 All four P2 fixes verified in the current head:

P2 item Verification
Idle extension cap MAX_STREAM_IDLE_EXTENSIONS = 3 (Line 143) consumed at Line 896; req.destroy('stream idle timeout') fires at Line 920 once the cap is exceeded ✅
Upstream destroy on client disconnect res.on('close', ...) at Line 851 → state.upstreamReq.destroy(new Error('client aborted')) at Line 854 ✅
Language fallback from available list normalizeLang at Line 29 derives fallback from available[0].code with 'zh' only as a last-resort safety net ✅
JSON fallback SSE header delay Covered by the lazy beginChatStreamResponsesSse machinery; pre-output JSON aborts remain retryable ✅

540/540 unit tests + 13/13 proxy shim tests passing on top of these is solid coverage. Kicking off the review now.

(ᵔᴥᵔ)🐇

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@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.

♻️ Duplicate comments (1)
web-ui/res/web-ui-render.precompiled.js (1)

132-137: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Keep the current language in the button accessible name.

At Line 135 and Line 425, aria-label overrides visible text, so assistive tech won’t read the current language that is shown in settings.language.sideLabel(...).

Suggested fix
-            "aria-label": _ctx.t('settings.language.open'),
+            "aria-label": _ctx.t('settings.language.sideLabel', { language: _ctx.currentLanguageLabel() }),

Also applies to: 422-427

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

In `@web-ui/res/web-ui-render.precompiled.js` around lines 132 - 137, The button's
aria-label currently uses _ctx.t('settings.language.open') which overrides the
visible text and hides the current language; update the aria-label on the
_createElementVNode call(s) that render the language settings button (the
instance using _ctx.openLanguageSettings and displaying
_ctx.t('settings.language.sideLabel', { language: _ctx.currentLanguageLabel()
})) so it includes the current language—e.g., build the label by combining the
open text with _ctx.currentLanguageLabel() or the same sideLabel translation—so
assistive tech announces both the action and the current language.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@web-ui/res/web-ui-render.precompiled.js`:
- Around line 132-137: The button's aria-label currently uses
_ctx.t('settings.language.open') which overrides the visible text and hides the
current language; update the aria-label on the _createElementVNode call(s) that
render the language settings button (the instance using
_ctx.openLanguageSettings and displaying _ctx.t('settings.language.sideLabel', {
language: _ctx.currentLanguageLabel() })) so it includes the current
language—e.g., build the label by combining the open text with
_ctx.currentLanguageLabel() or the same sideLabel translation—so assistive tech
announces both the action and the current language.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9927b629-47dc-4ea4-b7a2-e628f244615a

📥 Commits

Reviewing files that changed from the base of the PR and between c35d11e and d745dd2.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (13)
  • cli/builtin-proxy.js
  • package.json
  • tests/unit/builtin-proxy-responses-shim.test.mjs
  • web-ui/modules/app.computed.main-tabs.mjs
  • web-ui/modules/app.methods.providers.mjs
  • web-ui/modules/app.methods.runtime.mjs
  • web-ui/modules/config-mode.computed.mjs
  • web-ui/modules/i18n.dict.mjs
  • web-ui/modules/i18n.mjs
  • web-ui/partials/index/modals-basic.html
  • web-ui/partials/index/panel-config-codex.html
  • web-ui/partials/index/panel-trash.html
  • web-ui/res/web-ui-render.precompiled.js
✅ Files skipped from review due to trivial changes (1)
  • web-ui/modules/app.methods.providers.mjs
🚧 Files skipped from review as they are similar to previous changes (6)
  • package.json
  • web-ui/modules/app.methods.runtime.mjs
  • web-ui/partials/index/modals-basic.html
  • web-ui/modules/config-mode.computed.mjs
  • web-ui/modules/app.computed.main-tabs.mjs
  • web-ui/modules/i18n.dict.mjs
📜 Review details
🧰 Additional context used
🪛 HTMLHint (1.9.2)
web-ui/partials/index/panel-config-codex.html

[error] 129-129: Special characters must be escaped : [ > ].

(spec-char-escape)

web-ui/partials/index/panel-trash.html

[error] 2-2: Doctype must be declared before any non-comment content.

(doctype-first)

🔇 Additional comments (29)
web-ui/modules/i18n.mjs (7)

16-19: LGTM!


21-24: LGTM!


26-31: LGTM!


33-40: LGTM!


53-60: LGTM!


61-91: LGTM!


92-101: LGTM!

cli/builtin-proxy.js (13)

137-137: LGTM!


142-143: LGTM!


836-866: LGTM!


891-934: LGTM!


948-952: LGTM!


955-979: LGTM!


995-1018: LGTM!


1020-1039: LGTM!


1042-1060: LGTM!


1062-1073: LGTM!


1080-1084: LGTM!


1382-1387: LGTM!


1540-1551: LGTM!

tests/unit/builtin-proxy-responses-shim.test.mjs (6)

110-127: LGTM!


564-621: LGTM!


623-683: LGTM!


685-730: LGTM!


732-791: LGTM!


793-844: LGTM!

web-ui/res/web-ui-render.precompiled.js (1)

1267-1305: LGTM!

Also applies to: 1349-1377, 4195-4372, 5665-5742

web-ui/partials/index/panel-config-codex.html (1)

125-125: LGTM!

Also applies to: 129-129, 143-143

web-ui/partials/index/panel-trash.html (1)

17-18: LGTM!

Also applies to: 24-24, 33-34, 41-42, 45-45, 49-49, 66-66, 69-69, 77-77

@awsl233777
Copy link
Copy Markdown
Collaborator Author

Updated the built-in proxy retry behavior after re-checking the stream retry boundary:

  • keep transient upstream retry at initial attempt + 3 retries for /v1/responses → chat fallback conversion
  • preserve a single downstream Responses SSE lifecycle while retrying pre-output upstream stream failures
  • only emit response.failed without retry once real output/tool delta has already been sent downstream, avoiding duplicate generation/events
  • keep client-disconnect handling as an upstream abort, not a retry
  • removed the earlier stream idle cap path to avoid treating long Codex thinking streams as failed

Validation run locally:

  • node --test tests/unit/builtin-proxy-responses-shim.test.mjs — 12/12 passing
  • npm run test:unit — 539 tests passing

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "---IDLE TIMEOUT / EXTENSION CAP---"
grep -n "STREAM_IDLE\|idleExtension\|MAX_IDLE\|streamAccepted\|setTimeout\|maxStreamIdle" cli/builtin-proxy.js | head -40

echo "---CLIENT DISCONNECT / DESTROY---"
grep -n "destroy\|\.on('close\|\.on(\"close\|client aborted\|upstreamReq" cli/builtin-proxy.js | head -30

echo "---RETRY LOGIC---"
grep -n "TRANSIENT_RETRY\|retryTransient\|retry\|started\|finished" cli/builtin-proxy.js | head -40

echo "---STATE FLAGS---"
grep -n "state\.started\|state\.finished\|streamState\|closeListenerAttached" cli/builtin-proxy.js | head -30

echo "---TEST COUNT---"
grep -n "test\|suite\|describe\|it(" tests/unit/builtin-proxy-responses-shim.test.mjs | grep -i "^[0-9]" | wc -l
grep -c "^test\|^  test\|suite\|describe" tests/unit/builtin-proxy-responses-shim.test.mjs

Length of output: 5435



@awsl233777 Verified the current head. Here's the quick assessment:

No P0/P1 blockers.

Invariant Status
Pre-output retry (initial + 3 more via TRANSIENT_RETRY_DELAYS_MS) ✅ Line 141/1073
Single downstream SSE lifecycle across retries (sharedState / streamState passed through) ✅ Lines 916/1071
Mid-flight response.failed guard (streamState.outputStarted check) ✅ Lines 946–948
Client-disconnect upstream destroy (res.on('close')state.upstreamReq.destroy) ✅ Lines 851–854
response.created emitted exactly once per outer lifecycle state.started guard at line 837

On the idle-cap removal: The accepted stream timeout is now fully disabled via req.setTimeout(0) at line 940 (the handler at line 1054 becomes a no-op for accepted streams). This removes the "indefinite stall" protection for accepted streams. The client-disconnect destroy mitigates the most important case; the residual risk is a server-side half-open TCP connection with no client disconnect. Given that long Codex thinking sessions are the primary use-case here, the tradeoff is reasonable and you've consciously accepted it. Worth a comment in the code noting this is intentional.

Test count drop (13→12 / 540→539): Expected — the idle-extension-cap regression test was removed along with the feature. The remaining 12 shim tests cover the critical paths.

Kicking off the review now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@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.

🧹 Nitpick comments (1)
cli/builtin-proxy.js (1)

1071-1073: 💤 Low value

Consider explicit heartbeat cleanup on final retry failure.

When all retries fail and the caller's error handler writes response.failed directly to res, the heartbeat interval continues running until its next tick (up to 15 seconds later) when it notices res.writableEnded. The timer is unref'd so it won't block process exit, but for cleaner resource management, consider returning streamState in the result so the caller can explicitly stop the heartbeat.

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

In `@cli/builtin-proxy.js` around lines 1071 - 1073, The current retry loop
creates a heartbeat via createChatStreamResponsesSseState and calls
retryTransientRequest(() => streamChatCompletionsAsResponsesSse(...)) but on
final failure the caller only receives the error and cannot stop the heartbeat;
change the call site and the underlying functions so that when
retryTransientRequest returns a failure it includes the streamState (e.g.,
return { error, streamState } or similar), and update
streamChatCompletionsAsResponsesSse and retryTransientRequest to propagate
streamState on both success and failure so the caller can explicitly stop the
heartbeat (use the existing streamState variable created from
createChatStreamResponsesSseState and ensure callers can call its stop/cleanup
method).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@cli/builtin-proxy.js`:
- Around line 1071-1073: The current retry loop creates a heartbeat via
createChatStreamResponsesSseState and calls retryTransientRequest(() =>
streamChatCompletionsAsResponsesSse(...)) but on final failure the caller only
receives the error and cannot stop the heartbeat; change the call site and the
underlying functions so that when retryTransientRequest returns a failure it
includes the streamState (e.g., return { error, streamState } or similar), and
update streamChatCompletionsAsResponsesSse and retryTransientRequest to
propagate streamState on both success and failure so the caller can explicitly
stop the heartbeat (use the existing streamState variable created from
createChatStreamResponsesSseState and ensure callers can call its stop/cleanup
method).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1b64cc2a-f3fd-4067-bffb-2932c8f73c94

📥 Commits

Reviewing files that changed from the base of the PR and between d745dd2 and 4782fe3.

📒 Files selected for processing (2)
  • cli/builtin-proxy.js
  • tests/unit/builtin-proxy-responses-shim.test.mjs
📜 Review details
🔇 Additional comments (7)
tests/unit/builtin-proxy-responses-shim.test.mjs (1)

113-113: LGTM!

Also applies to: 558-616, 680-725

cli/builtin-proxy.js (6)

137-141: LGTM!

Also applies to: 161-161


715-716: LGTM!

Also applies to: 801-801


836-866: LGTM!


876-897: LGTM!


916-916: LGTM!

Also applies to: 1010-1014


947-968: LGTM!

Copy link
Copy Markdown
Contributor

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

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

Inline comments:
In `@web-ui/styles/openclaw-structured.css`:
- Around line 128-131: The keyframe name accordionSlide violates the kebab-case
keyframes-name-pattern; rename the keyframe and its usage together (e.g.,
accordion-slide) by updating the `@keyframes` declaration name and any references
to it in the stylesheet (for example the animation: property that currently uses
accordionSlide) so both match the new kebab-case identifier.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e357d674-24b7-46c3-8d3c-89c34436c727

📥 Commits

Reviewing files that changed from the base of the PR and between 8610e93 and 2367c1a.

📒 Files selected for processing (8)
  • tests/unit/web-ui-behavior-parity.test.mjs
  • web-ui/app.js
  • web-ui/modules/app.methods.openclaw-editing.mjs
  • web-ui/partials/index/modal-openclaw-config.html
  • web-ui/partials/index/panel-config-openclaw.html
  • web-ui/res/web-ui-render.precompiled.js
  • web-ui/styles/controls-forms.css
  • web-ui/styles/openclaw-structured.css
✅ Files skipped from review due to trivial changes (1)
  • web-ui/styles/controls-forms.css
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/unit/web-ui-behavior-parity.test.mjs
  • web-ui/partials/index/modal-openclaw-config.html
  • web-ui/modules/app.methods.openclaw-editing.mjs
  • web-ui/partials/index/panel-config-openclaw.html
  • web-ui/app.js
📜 Review details
🧰 Additional context used
🪛 Stylelint (17.12.0)
web-ui/styles/openclaw-structured.css

[error] 131-131: Expected keyframe name "accordionSlide" to be kebab-case (keyframes-name-pattern)

(keyframes-name-pattern)

🔇 Additional comments (1)
web-ui/styles/openclaw-structured.css (1)

168-176: Duplicate .form-hint.hint-error rule remains.

This selector is still defined twice with identical declarations; keep only one block.

Comment on lines +128 to +131
animation: accordionSlide 0.2s ease;
}

@keyframes accordionSlide {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix keyframe naming to satisfy stylelint rule.

accordionSlide violates the configured keyframes-name-pattern (kebab-case). Rename the keyframe and its usage together.

Proposed fix
 .accordion-content {
     padding: 0 16px 16px;
-    animation: accordionSlide 0.2s ease;
+    animation: accordion-slide 0.2s ease;
 }
 
-@keyframes accordionSlide {
+@keyframes accordion-slide {
     from {
         opacity: 0;
         transform: translateY(-8px);
     }
📝 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
animation: accordionSlide 0.2s ease;
}
@keyframes accordionSlide {
animation: accordion-slide 0.2s ease;
}
`@keyframes` accordion-slide {
🧰 Tools
🪛 Stylelint (17.12.0)

[error] 131-131: Expected keyframe name "accordionSlide" to be kebab-case (keyframes-name-pattern)

(keyframes-name-pattern)

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

In `@web-ui/styles/openclaw-structured.css` around lines 128 - 131, The keyframe
name accordionSlide violates the kebab-case keyframes-name-pattern; rename the
keyframe and its usage together (e.g., accordion-slide) by updating the
`@keyframes` declaration name and any references to it in the stylesheet (for
example the animation: property that currently uses accordionSlide) so both
match the new kebab-case identifier.

- Remove '提示:应用后写入 ~/.openclaw/openclaw.json' hint text
- Keep workspace card functionality intact
@ymkiux ymkiux force-pushed the feat/settings-language-selector branch from 05beed7 to f5245bc Compare May 30, 2026 14:25
ymkiux added 2 commits May 30, 2026 22:48
- Reduce layout gap from 16px to 12px
- Reduce tools grid gap from 12px to 8px
- Reduce button padding from 16px to 12px
- Reduce button content gap from 12px to 10px
- Remove '选择常用配置,或进入编辑器维护完整 JSON5' hint
Copy link
Copy Markdown
Contributor

@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.

♻️ Duplicate comments (2)
web-ui/styles/openclaw-structured.css (2)

126-132: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Rename keyframe to kebab-case to satisfy lint.

Line 128 and Line 131 still use accordionSlide, which violates the configured keyframe naming rule and can fail stylelint.

Proposed minimal fix
 .accordion-content {
     padding: 0 16px 16px;
-    animation: accordionSlide 0.2s ease;
+    animation: accordion-slide 0.2s ease;
 }
 
-@keyframes accordionSlide {
+@keyframes accordion-slide {
     from {
         opacity: 0;
         transform: translateY(-8px);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web-ui/styles/openclaw-structured.css` around lines 126 - 132, The keyframe
name accordionSlide violates the kebab-case lint rule; rename the keyframe to
kebab-case (e.g., accordion-slide) and update all references: change the
`@keyframes` declaration name and the .accordion-content animation property value
(animation: accordion-slide 0.2s ease;) so the `@keyframes` rule and the animation
usage (and any from/to blocks) match the new kebab-case identifier.

168-176: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove duplicated .form-hint.hint-error rule.

The same selector is declared twice with identical declarations (Line 169-171 and Line 174-176), which is redundant and noisy.

Proposed minimal fix
 /* Form hint error state */
 .form-hint.hint-error {
     color: var(--color-error);
 }
-
-/* Form hint error state */
-.form-hint.hint-error {
-    color: var(--color-error);
-}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web-ui/styles/openclaw-structured.css` around lines 168 - 176, Duplicate CSS
rule .form-hint.hint-error is declared twice; remove the redundant block so only
a single .form-hint.hint-error { color: var(--color-error); } remains (locate
the two identical selectors .form-hint.hint-error in openclaw-structured.css and
delete the second/duplicate declaration).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@web-ui/styles/openclaw-structured.css`:
- Around line 126-132: The keyframe name accordionSlide violates the kebab-case
lint rule; rename the keyframe to kebab-case (e.g., accordion-slide) and update
all references: change the `@keyframes` declaration name and the
.accordion-content animation property value (animation: accordion-slide 0.2s
ease;) so the `@keyframes` rule and the animation usage (and any from/to blocks)
match the new kebab-case identifier.
- Around line 168-176: Duplicate CSS rule .form-hint.hint-error is declared
twice; remove the redundant block so only a single .form-hint.hint-error {
color: var(--color-error); } remains (locate the two identical selectors
.form-hint.hint-error in openclaw-structured.css and delete the second/duplicate
declaration).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c822b6c7-345a-401a-a994-d090694dcad9

📥 Commits

Reviewing files that changed from the base of the PR and between f5245bc and 9cdb0e4.

📒 Files selected for processing (3)
  • web-ui/partials/index/panel-config-openclaw.html
  • web-ui/res/web-ui-render.precompiled.js
  • web-ui/styles/openclaw-structured.css
💤 Files with no reviewable changes (1)
  • web-ui/partials/index/panel-config-openclaw.html
📜 Review details
🔇 Additional comments (1)
web-ui/styles/openclaw-structured.css (1)

357-383: LGTM!

ymkiux and others added 11 commits May 30, 2026 23:11
- Change tools grid from 2-column to single column flex
- AGENTS.md button and workspace file now stack vertically
- Change card layout from horizontal to vertical
- Icon/content and pill/actions now stack top to bottom
- Hide card icon (first letter circle) from config cards
- Remove left visual anchor for cleaner look
- Remove 'OpenClaw 配置' heading from config card
- Remove white-space/overflow constraints on title and subtitle
- Allow card content to use full available width
- Card text now wraps naturally instead of truncating
- Reduce workspace card padding from 16px to 12px/14px
- Add width: 100% to input group and form input
- Add width: 100% to tool button
- Elements now use full available width
- Add explicit padding to outer workspace/config sections
- Remove duplicate styles from nested workspace card
- Distinguish outer section card from nested card with specific selectors
- Ensure x-position alignment between workspace elements and config cards
@awsl233777
Copy link
Copy Markdown
Collaborator Author

Updated this PR with a metapi-style hardening pass for the built-in /v1/responses → chat fallback conversion.

What changed:

  • aggregate consecutive Responses function_call / custom_tool_call items into one assistant tool_calls message before emitting tool outputs
  • drop orphaned Responses tool outputs instead of forwarding invalid chat tool messages
  • preserve reasoning summary/signature items, developer role compatibility, richer content blocks, text.formatresponse_format, text.verbosity, and parallel_tool_calls
  • keep existing Codexmate image URL compatibility by wrapping string input_image URLs as { url }
  • added a regression test covering the metapi-style conversion shape

Validated:

  • node --test tests/unit/builtin-proxy-responses-shim.test.mjs (13/13 passed)
  • npm run test:unit (540/540 passed)

@coderabbitai please re-review the latest head, focusing on the Responses/chat conversion contract and any compatibility regressions in provider payload forwarding.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 30, 2026

@awsl233777 This appears to be a duplicate of an earlier message in this PR — the identical metapi-style hardening comment was already reviewed and addressed earlier in this conversation thread.

To recap the outcome of that earlier review and everything that followed:

That review result (no P0/P1 blockers):

  • Consecutive tool call aggregation, orphan tool output stripping, reasoning preservation, developersystem, text.formatresponse_format, input_image string-URL wrapping — all verified ✅
  • Two P2 suggestions were given (bare-string input_image regression test, comment on non-consecutive aggregation boundary) → both addressed in b1d3833

Since then, the PR has accumulated many more verified fixes:

  • Namespace depth cap (MAX_RESPONSES_TOOL_NAMESPACE_DEPTH = 5) in both builtin-proxy.js and openai-bridge.js
  • Streaming bottleneck fix (streaming /v1/responses bypasses upstream probe)
  • apply_patch explicit function_call round-trip preserved in both CLI files
  • Client-disconnect upstream destroy, language fallback from available languages
  • Reset workflow interactive prompt, release changelog tooling
  • OpenClaw/Doctor UI polish, credential visibility toggles

Current state: 556/556 tests passing, no P0/P1 blockers. Safe to merge.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@awsl233777
Copy link
Copy Markdown
Collaborator Author

Follow-up UI polish after visual review:

  • Reworked the OpenClaw config tab granularity to align better with the other config tabs:
    • removed the narrow centered island layout;
    • weakened outer wrapper cards so the visible units are the actual config items;
    • laid out AGENTS.md and workspace-file controls as same-level cards on wider screens;
    • kept the mobile layout stacked while preserving the existing config status pill and edit action in a compact row.
  • No DOM elements were added or removed; this round is CSS-only.

Validation actually run after this follow-up:

  • Browser visual QA for OpenClaw at 1440px / 900px / 500px.
  • Confirmed no raw i18n keys, no obvious horizontal overflow, no clipped controls, and mobile status/action layout remains visible.
  • npm run test:unit → all 563 tests passed.
  • npm run lint → passed for 204 files.
  • npm run test:e2e → passed.

@coderabbitai re-review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 30, 2026

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== openclaw-structured.css current state ==="
cat web-ui/styles/openclaw-structured.css

echo ""
echo "=== panel-config-openclaw.html - aria-label on card actions ==="
grep -n "aria-label\|applyAria\|editAria\|deleteAria\|btn-mini\|openclaw-card-actions" web-ui/partials/index/panel-config-openclaw.html | head -30

echo ""
echo "=== precompiled bundle - openclaw section layout classes ==="
grep -n "openclaw-workspace-card\|openclaw-tools-grid\|openclaw-tool-btn\|openclaw-action-grid\|openclaw-hero\|openclaw-overview\|openclaw-primary\|openclaw-configs" web-ui/res/web-ui-render.precompiled.js | head -40

echo ""
echo "=== responsive.css - openclaw breakpoints ==="
grep -n "openclaw\|980px\|820px" web-ui/styles/responsive.css | head -30

echo ""
echo "=== Check for any conflict markers ==="
rg -n "<<<<<<< |>>>>>>> |=======" web-ui/styles/ 2>/dev/null | head -10

Length of output: 15738



@awsl233777 CSS-only polish in the latest commit verified. No P0/P1 blockers.

Check Status
Outer wrapper cards transparent/borderless (.openclaw-workspace-card.settings-card, .openclaw-configs-card) border: 0; background: transparent; box-shadow: none
Inner nested workspace card retains distinct pill (border, border-radius, background)
.openclaw-tools-grid side-by-side at ≥820px, stacked below repeat(auto-fit, minmax(300px, 1fr))1fr at ≤820px
Mobile status/action layout preserved (≤520px block)
No conflict markers
Precompiled bundle in sync
563/563 unit + lint + e2e

Still outstanding: P2 — applyAria aria-label

panel-config-openclaw.html Line 69 has editAria ✅ and Line 75 has deleteAria ✅, but the apply card action button still has no :aria-label binding. The openclaw.action.applyAria translation keys are present in all three locales. This remains the only unresolved item from the merge-resolution regression. Would you like to wire it up in a follow-up commit?


P3 — form-hint.hint-error rule duplicated in openclaw-structured.css

The selector appears twice (pre-existing). Not a blocker but harmless to deduplicate whenever convenient.

(ᵔᴥᵔ)🐇

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@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.

Caution

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

⚠️ Outside diff range comments (1)
web-ui/styles/openclaw-structured.css (1)

558-564: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clamp won’t apply with visible overflow.

-webkit-line-clamp: 2 at Line 562 won’t reliably truncate while overflow is set to visible in the earlier .card-subtitle rule (Line 540). Set overflow to hidden in the clamped rule.

Proposed fix
 .openclaw-card-list .card-subtitle {
     white-space: normal;
     display: -webkit-box;
     -webkit-box-orient: vertical;
     -webkit-line-clamp: 2;
+    overflow: hidden;
     overflow-wrap: anywhere;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web-ui/styles/openclaw-structured.css` around lines 558 - 564, The
.openclaw-card-list .card-subtitle rule uses -webkit-line-clamp: 2 but the
earlier .card-subtitle rule sets overflow: visible, preventing clamping; update
the clamped rule (selector .openclaw-card-list .card-subtitle) to set overflow:
hidden (and optionally add text-overflow: ellipsis; white-space: normal stays)
so -webkit-line-clamp can apply, or remove/override the conflicting overflow:
visible in the base .card-subtitle rule.
♻️ Duplicate comments (1)
web-ui/styles/openclaw-structured.css (1)

128-131: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Rename keyframe to kebab-case to satisfy lint.

Line 128 and Line 131 still use accordionSlide; this continues to violate the configured keyframe naming rule and can fail stylelint.

Proposed fix
 .accordion-content {
     padding: 0 16px 16px;
-    animation: accordionSlide 0.2s ease;
+    animation: accordion-slide 0.2s ease;
 }

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

In `@web-ui/styles/openclaw-structured.css` around lines 128 - 131, The keyframe
name accordionSlide violates the kebab-case lint rule; rename the `@keyframes`
declaration from accordionSlide to a kebab-case name (e.g., accordion-slide) and
update the animation property usage (animation: accordionSlide 0.2s ease;) to
use the new kebab-case name (animation: accordion-slide 0.2s ease;), and search
for any other occurrences of the symbol accordionSlide in the stylesheet to
replace them consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@web-ui/styles/openclaw-structured.css`:
- Around line 558-564: The .openclaw-card-list .card-subtitle rule uses
-webkit-line-clamp: 2 but the earlier .card-subtitle rule sets overflow:
visible, preventing clamping; update the clamped rule (selector
.openclaw-card-list .card-subtitle) to set overflow: hidden (and optionally add
text-overflow: ellipsis; white-space: normal stays) so -webkit-line-clamp can
apply, or remove/override the conflicting overflow: visible in the base
.card-subtitle rule.

---

Duplicate comments:
In `@web-ui/styles/openclaw-structured.css`:
- Around line 128-131: The keyframe name accordionSlide violates the kebab-case
lint rule; rename the `@keyframes` declaration from accordionSlide to a kebab-case
name (e.g., accordion-slide) and update the animation property usage (animation:
accordionSlide 0.2s ease;) to use the new kebab-case name (animation:
accordion-slide 0.2s ease;), and search for any other occurrences of the symbol
accordionSlide in the stylesheet to replace them consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 752da7fa-f233-41fb-ba2f-f5ddcd52e9e9

📥 Commits

Reviewing files that changed from the base of the PR and between 4e8ca4f and 188dd35.

📒 Files selected for processing (1)
  • web-ui/styles/openclaw-structured.css
📜 Review details
🔇 Additional comments (1)
web-ui/styles/openclaw-structured.css (1)

169-176: Remove duplicated .form-hint.hint-error declaration.

Line 169-171 and Line 174-176 define the same selector/property pair; keep only one block.

@awsl233777
Copy link
Copy Markdown
Collaborator Author

@coderabbitai Please do one focused follow-up pass on the latest head and answer with:

  1. Any remaining P0/P1 issues that must be fixed before merge.
  2. If there are no P0/P1 blockers, say that explicitly.
  3. A short list of non-blocking improvement suggestions, especially for the OpenClaw config tab UI/accessibility and the recent reset workflow changes.

Please avoid repeating already-resolved or duplicate findings unless they are still valid on the current head.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 30, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== applyAria on card actions ==="
grep -n "applyAria\|aria-label.*apply\|apply.*aria-label\|btn-mini\|openclaw-card-actions" web-ui/partials/index/panel-config-openclaw.html | head -30

echo ""
echo "=== Precompiled bundle - openclaw card apply aria-label ==="
grep -n "applyAria\|openclawAction\|openclaw.*action.*apply" web-ui/res/web-ui-render.precompiled.js | head -20

echo ""
echo "=== openclaw.action.applyAria in i18n dict ==="
grep -n "applyAria" web-ui/modules/i18n.dict.mjs | head -10

echo ""
echo "=== config-tabs-ui.test.mjs - openclaw apply aria-label assertion ==="
grep -n "applyAria\|aria-label.*apply\|openclaw.*action" tests/unit/config-tabs-ui.test.mjs | head -20

echo ""
echo "=== form-hint.hint-error duplicate in openclaw-structured.css ==="
grep -n "hint-error\|hint_error" web-ui/styles/openclaw-structured.css

echo ""
echo "=== Conflict markers check ==="
rg -n "<<<<<<< |>>>>>>> |=======" cli/ web-ui/ 2>/dev/null | grep -v ".git" | grep -v Binary | head -15

echo ""
echo "=== Proxy state machine guards still intact ==="
grep -n "state\.started\|state\.finished\|state\.upstreamReq\|state\.outputStarted" cli/builtin-proxy.js | head -15

echo ""
echo "=== Credential visibility resets ==="
grep -n "showAddProviderKey\|showAddClaudeConfigKey\|showEditProviderKey\|showEditClaudeConfigKey" web-ui/app.js | head -15

echo ""
echo "=== Test counts ==="
grep -c "^test(" tests/unit/builtin-proxy-responses-shim.test.mjs 2>/dev/null
grep -c "^test(" tests/unit/openai-bridge-upstream-responses.test.mjs 2>/dev/null
grep -c "^test(" tests/unit/reset-main.test.mjs 2>/dev/null

echo ""
echo "=== Doctor panel - navigate guard and export availability ==="
grep -n "navigate\|action\.target\|healthCheckResult\.report\|export" web-ui/partials/index/panel-dashboard.html | head -30

echo ""
echo "=== hardcoded Cancel string in OpenClaw modal footer ==="
grep -n "取消\|Cancel\|cancel" web-ui/partials/index/modal-openclaw-config.html | head -10

echo ""
echo "=== resolveResetTargetPrNumber - destructive reset gate ==="
grep -n "buildResetPlan\|executeResetPlan\|resolveResetTarget\|hasResetTargetArg\|main\b" tools/dev/reset-main.js | head -30

Length of output: 8604



@awsl233777 Here is the focused final pass on the current head.


1. ✅ No P0/P1 blockers

All critical invariants hold on the current head:

Area Status
No git conflict markers ✅ All ===== in CSS files are section-divider comments
Proxy streaming state machine (state.started, state.finished, state.upstreamReq, state.outputStarted) ✅ All guards present
Credential visibility defaults (showAddProviderKey, showAddClaudeConfigKey, showEditProviderKey, showEditClaudeConfigKey) ✅ All four in app.js Lines 65–72
Doctor navigate guard + export availability ✅ Triple-guarded navigate; export gated on report presence only (not issues.length)

2. Previously open P2 — now resolved ✅

applyAria aria-label is now wired up: panel-config-openclaw.html Line 56 has :aria-label="t('openclaw.action.applyAria', { name })" and the precompiled bundle Line 2038 matches. All three locale entries (zh, ja, en) were already present. This was the last open P2.


3. Non-blocking improvement suggestions

P2 — config-tabs-ui.test.mjs missing applyAria assertion

The test file asserts editAria (Line 348) and deleteAria (Line 353) on card action buttons, but has no corresponding assertion for applyAria. The template and bundle have it — this is a minor coverage gap, not a functional regression. Suggest adding:

assert.match(html, /openclaw\.action\.applyAria/);

P3 — Hardcoded 取消 in modal-openclaw-config.html Line 335

The OpenClaw modal edit footer Cancel button still uses a hardcoded Chinese string:

<button class="btn btn-cancel" `@click`="closeOpenclawConfigModal" ...>取消</button>

t('common.cancel') is already present in all three locales (used elsewhere in the UI), so a one-line template fix would complete the i18n pass for this file.

P3 — form-hint.hint-error duplicated in openclaw-structured.css Lines 169 and 174

The selector appears twice with identical rules. Safe to deduplicate when convenient.

P3 — reset prompt wording: empty-input path is implicit

resolveResetTargetPrNumber prints "Press Enter to reset to origin/main, or type a PR number..." but the Enter-→-main behavior is implicit (empty string returned from readline). The behavior is correct and tested; a brief follow-up note in the tools/dev/reset-main.js module comment explaining this contract would help future contributors.


No P0/P1 blockers remain. Safe to merge.

(ᵔᴥᵔ)🎉

@ymkiux ymkiux merged commit 6315a17 into main May 30, 2026
9 checks passed
@ymkiux ymkiux deleted the feat/settings-language-selector branch May 30, 2026 17:23
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