* feat(db): in-app recovery screen with upgrade button when DB version is too new
When the SQLite user_version is newer than the app supports (SCHEMA_VERSION),
Database::init() fails and previously dead-ended in a native Retry/Exit dialog
(Retry just fails again). The app now boots a dedicated recovery screen instead.
- Detect the recoverable "version too new" case and surface it via init_status
(kind="db_version_too_new" + db_version/supported_version); force-show the main
window and skip normal AppState boot (recovery commands need only AppHandle).
- The recovery screen first checks for an available update:
- update available -> "Upgrade app" runs the updater (download + install +
restart) with a download progress bar.
- no update (already latest) -> warns that the DB is too new even for the latest
build (likely a third-party client), so upgrading cannot help.
- install_update_and_restart now emits `update-download-progress` events; new
`check_app_update_available` command.
- i18n: en / ja / zh / zh-TW.
Verified: pnpm typecheck + format:check + test:unit (351) green; cross-model
review of Rust correctness and recovery-mode safety (no AppState panic).
* fix(db): pre-check too-new DB before schema writes; exit on close in recovery mode
Addresses review feedback on the too-new-DB recovery flow.
- P1: stored_user_version_exceeds_supported() is now checked BEFORE
Database::init(), so create_tables()'s DDL (incl. the unconditional
DROP INDEX/DROP TABLE IF EXISTS failover_queue) never runs against a
database whose user_version we cannot understand. The earlier
post-init-failure recovery branch is removed; the pre-check is the
single authoritative guard, so "don't write to a DB we can't read"
now holds from the very first DB access.
- P2: the window CloseRequested handler now detects recovery mode
(init_error kind = db_version_too_new) and exits the app instead of
honoring minimize_to_tray_on_close. Recovery mode returns before the
tray is created, so hiding the window would leave the app running with
no tray to bring it back; native-close now quits cleanly.
Verified: cargo fmt --check clean; cross-model Rust review of both fixes
(compile-correctness + no normal-startup/close regression).
* add glm-5.2 pricing
* feat(usage): add live end time option for custom date range
Add a "End time follows current time" checkbox in the custom date range
picker. When enabled, the end time becomes read-only and automatically
tracks the current moment, so usage data always reflects up-to-the-second
consumption from the chosen start time.
This is especially useful under the Coding Plan 5-hour quota window —
users can set the start time to when their 5h window began and keep
the end time live to monitor real-time token consumption.
* style(usage): fix prettier formatting for UsageDashboard
* fix(usage): include liveEndTime in React Query cache keys
Without liveEndTime in the query key, a live custom range and a fixed
custom range with the same stored endpoints share the same cache entry.
After the live range refreshes (endDate = now), switching to the fixed
range shows stale data fetched through "now" instead of the original
end time.
* ci: add Windows ARM64 release artifacts
* ci: keep release matrix jobs independent
* ci: fix pnpm cache path on Windows runners
* ci: setup pnpm with corepack on Windows ARM64
* ci: fix Windows ARM64 release build
* ci: retry transient release bundler downloads
* ci: remove non-minimal release workflow changes
* ci: keep release matrix jobs independent
macOS signing fails in forks without Apple secrets and, with default
matrix fail-fast, cancels the sibling jobs (including Windows ARM64)
before they finish. Disable fail-fast so each platform runs to completion.
---------
Co-authored-by: MoonDreamStars <moondreamstar1@gmail.com>
Set the default Claude Code auto-compact window to 262144 for the
Kimi For Coding provider preset, matching the official Kimi docs:
https://www.kimi.com/code/docs/en/third-party-tools/other-coding-agents.html
Use templateValues so users can customize the value (e.g. for future
models or performance tuning) while keeping 262144 as the default.
Append a clickable CTA hook to the end of the Kimi K2.6 slogan in all
four README locales (en/zh/ja/de), pointing to the existing
aff=cc-switch Moonshot console link.
CI cargo fmt --check failed on transform_codex_chat.rs (the wrapping
introduced upstream did not match rustfmt 1.95.0). Apply cargo fmt so the
chat_legacy_function_call_to_response_item call uses the expected layout.
The official-first/prime-partner sort changed Original mode order, but the
selector tests still asserted the old raw array order. Update the three
stale assertions and add a dedicated case covering the prime-partner group
and the exclusivity rule (an official preset also flagged primePartner
stays only in the official group).
Query coding-plan / agent-plan usage for Volcengine Ark via the
control-plane OpenAPI (open.volcengineapi.com), which requires
account-level AccessKey signing rather than the inference API key.
- Implement Volcengine Signature V4 (an AWS SigV4 variant: fixed
canonical header order, "HMAC-SHA256" algorithm without the AWS4
prefix, scope ending in "request", service "ark") in
services/coding_plan.rs
- Auto-detect the plan by probing GetAFPUsage (Agent Plan, AFP
five-hour/weekly/monthly quotas) first, falling back to
GetCodingPlanUsage (Coding Plan, session/weekly/monthly percentages);
a single credential covers whichever plan the account holds
- Parse the real response shape: the window label lives in the `Level`
field; guard ResetTimestamp <= 0 (session returns -1)
- Store account-level credentials on UsageScript (accessKeyId /
secretAccessKey), threaded through both the test command and the
TOKEN_PLAN auto-refresh path
- Add an independent AK/SK input block with a detailed hint pointing to
the Volcengine console -> API Access Keys
- Add the `monthly` tier label (frontend TIER_I18N_KEYS + tray
TIER_LABEL_GROUPS + subscription service)
- Sync zh / en / ja / zh-TW locales and the usage-query docs
- Reduce title->tabs and tabs->card vertical gaps from 24px to 12px
- Add an always-visible footer hint guiding users to fill the fields
below after choosing a preset (pinned footer, never scrolls off)
- FullScreenPanel: add optional contentClassName prop to scope content
padding overrides without affecting the other panels that share it
- i18n: add provider.addFooterHint in zh/en/ja/zh-TW
The OpenCode and OpenClaw presets were labeled "Kimi K2.7 Code", unlike
the plain "Kimi" used by the other apps. Rename the preset (and the
OpenCode provider display name) to "Kimi" for consistency; the model
label stays "Kimi K2.7 Code" since it describes the actual model.
In the default (Original) sort mode, float official-category presets to
the very top, followed by prime partners (Kimi), then the rest in their
original array order. Implemented as a three-way partition+concat so each
group keeps its internal order and no preset is listed twice.
Replace the inline Kimi placeholder mark with the vendor's refreshed
logo. The K glyph uses `currentColor` so it follows the theme text color
(dark in light mode, white in dark mode), while the brand accent dot is
pinned to the new #1783FF. Align the metadata fallback color accordingly.
The vendor renamed its domain, API endpoints, and trademark from CTok to
ETok. Update all user-facing branding accordingly:
- Presets (claude, claude-desktop, codex, gemini, hermes, openclaw,
opencode): ctok.ai -> etok.ai, api.ctok.ai -> api.etok.ai, internal
provider id ctok -> etok, display name CTok.ai -> ETok.ai
- Icons: replace inline placeholder SVG with the new ETok brand PNG,
moved from `icons` to `iconUrls`; key ctok -> etok; banner logo
ctok.png -> etok.png
- README (en/zh/ja/de) partner banner: new logo, links, and copy
- i18n (en/ja/zh/zh-TW): partnerPromotion key ctok -> etok
Keep "ctok" in the Codex history-migration whitelist as a legacy id and
add "etok", so existing users' local session history stays correctly
bucketed after the rename.
* Chat API: skip tool calls with missing function names
Some providers send empty or absent function names in streaming
tool call deltas. Previously these produced invalid output items.
- Don't overwrite accumulated state.name with empty deltas
- Skip tool calls that never received a valid name (instead of
falling back to 'unknown_tool')
- Apply the same defensive guard in finalize_tools and the
non-streaming path
* Address review: defer empty-name skip to finalization
Require both call_id and name before triggering should_add,
instead of skipping eagerly when name is absent in the first
delta. This handles providers that send id before name,
as suggested in the Codex review.
* Guard legacy function_call against empty name
Return Option<Value> from chat_legacy_function_call_to_response_item,
returning None when function_call.name is missing or empty. This covers
the legacy message.function_call path that the original guard missed.
* Remove unreachable unknown_tool fallback
---------
Co-authored-by: Jason <farion1231@gmail.com>
Introduce an optional primePartner flag on all provider preset interfaces.
Regular partners keep the gold star badge; prime partners render a solid
gold heart (no badge frame). Mark the first-party Moonshot Kimi presets
(Kimi / Kimi For Coding / Kimi K2.7 Code) across claude, codex, claude
desktop, opencode, openclaw and hermes as prime partners.
Replace the low-res ccsub.jpg with a vector SVG. Letterbox it from
2046x648 (3.157:1) to 2046x850 (~2.406:1) so it matches the other
sponsor-table banners and renders at the same height (62px @ width=150).
Point all four README locales at the new asset.
The session-history migration only scanned ~/.codex/state_5.sqlite and the
config.toml `sqlite_home` location. When Codex's SQLite state is relocated via
the CODEX_SQLITE_HOME env var, the state DB was never scanned and its threads
kept their old provider bucket. Add a CODEX_SQLITE_HOME fallback (config
`sqlite_home` still takes precedence) in the shared codex_state_db_paths helper
used by both the third-party and unified-session history migrations, plus serial
env tests.
* feat(usage): support importing model pricing from models.dev
Add an "Import from models.dev" button to the Add Pricing panel that
fetches https://models.dev/api.json, lists priced models sorted by
release date (newest 50 by default, full-text search across ~4800),
and bulk-imports the selected entries through the same
update_model_pricing command as manual entry.
- Normalize imported model IDs to match the backend's
clean_model_id_for_pricing rules (strip vendor prefix, lowercase,
truncate ':' suffix, map '@' to '-', drop the [1m] marker) so the
stored rows actually match cost-attribution lookups
- Dedupe selections that collapse to the same model_id and report
skipped duplicates in the success toast
- Invalidate usage queries on settled (not just success) so partial
import failures still refresh the pricing list
- Keep ESC inside the picker's search input from closing the dialog
and discarding the selection
- Add i18n keys for zh/en/zh-TW/ja and unit tests for the
normalization, price formatting and flattening logic
Fixes#4017
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(usage): match scoped cost backfill against raw model aliases
The scoped backfill selected zero-cost rows via exact SQL string match,
but log columns store raw model strings (route prefixes, :free variants,
date suffixes), so alias rows were skipped until the next full backfill
on startup. Filter rows in Rust with the same model_pricing_candidates
normalization used by the pricing lookup; pricing decision logic is
untouched. Pre-existing gap from schema v11, surfaced by bulk import.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(usage): restrict models.dev pricing import to a single model
Each update_model_pricing call triggers a backfill pass that loads
every zero-cost usage row before filtering by model, so bulk-importing
N entries scaled as selectedModels x allZeroCostLogs full scans.
Re-importing pricing is rare, so drop the batch path instead of
optimizing it: the picker is now single-select, one import runs exactly
one update_model_pricing call and one backfill pass. This also removes
the normalized-ID dedup logic and the useImportModelPricing hook in
favor of the existing useUpdateModelPricing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(provider-preset-selector): after searching presets, it is impossible to select search results.
feat(provider-preset-selector): add keyboard shortcut for search input and improve focus handling.
* fix(provider-list): prevent keydown event from triggering when default is prevented
* fix(provider-preset): keep preset clickable after search and restore keyboard UX
- ProviderList: scope the typing guard to the Ctrl/Cmd+F branch so Escape
still closes the search panel (the top-level early return swallowed it);
reuse isTextEditableTarget instead of re-implementing the check.
- ProviderPresetSelector: drop the rAF select() that raced with typing and
ate the first character (gateway -> ateway), and restore the input
autoFocus for the open-by-click path; refocus via rAF when Ctrl/Cmd+F is
pressed while the box is already open so focus returns to the input.
- Add a regression test for the re-focus-on-shortcut behavior.
---------
Co-authored-by: Jason <farion1231@gmail.com>
* fix(terminal): detect user default shell instead of hardcoded bash
All macOS/Linux terminal launch functions in misc.rs hardcoded `bash` as
the execution shell, causing zsh users' .zshrc to not load. Now detect
the user's default shell via $SHELL (macOS fallback /bin/zsh, Linux
fallback /bin/bash) and use it consistently across shebang, AppleScript,
spawn args, and interactive shell flags.
- Add get_user_shell(), get_shell_name(), interactive_flags_for_shell()
- Replace 10 hardcoded bash locations with dynamic shell detection
- zsh gets --norcs --no-globalrcs, bash keeps --norc --noprofile
- run_tool_lifecycle_silently unchanged (bash-specific syntax)
- Fixes#1546, Related #2385
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(terminal): use POSIX sh for script execution, user shell for interactive session
Address code review issues:
1. Replace shell-specific `read -n 1 -s`/`read -k 1 -s` with POSIX `read -r _`
(press Enter to close) — ensures portability across bash/zsh/fish/dash
2. Unify AppleScript to use `sh` consistently (not mixing shell/shell_name)
3. Protect fish users: scripts always interpreted by POSIX sh, not fish
(fish doesn't support trap/exec). exec line switches to user's shell.
4. Use build_exec_line to avoid trailing space when interactive_flags is empty
All terminal launchers now execute scripts via `sh`. Script shebangs are
`#!/bin/sh`. The exec line at script end switches to the user's detected
shell with appropriate clean-start flags. Terminal launcher functions no
longer need shell/shell_name parameters — simplified signatures.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: validate user shell for terminal launch
* fix: load user shell config after terminal command
* test: align terminal shell exec expectations
* refactor(terminal): remove dead code, quote Warp path, unify shebangs
- Remove `interactive_flags_for_shell` (always returned "") and
`get_shell_name` (no longer needed after removing flags); simplify
`build_exec_line` to single-arg form
- Quote the Warp launcher script path with `shell_single_quote` for
consistency with the main launcher scripts
- Unify all script shebangs to `#!/usr/bin/env sh` for better
portability (e.g. NixOS where /bin/sh may not exist)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(terminal): fix linux shell exec helper call
* fix(terminal): run provider command through user shell
* fix(terminal): validate user shell executable
* fix(terminal): avoid interactive provider shell job
* fix(terminal): harden user shell launcher
Load zsh provider commands through the user's login and interactive environment, preserve launch cwd handling, and replace Terminal/iTerm launcher shells so the final user shell is restored cleanly.
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat(session-manager): show source file name in session detail header
Display the session log file name (with full path on hover) alongside
the project directory, so users can locate and copy the underlying
JSONL file directly from the UI.
* fix(session-manager): truncate long source file name in detail header
Long, space-less JSONL basenames (e.g. Codex rollout files at ~70 chars)
overflowed the flex meta row and bled into the action-button area on
narrow windows. Mirror the sibling project-dir span by capping the
filename at max-w-[200px] with truncation; the full path stays available
via the hover tooltip and click-to-copy.
---------
Co-authored-by: Jason <farion1231@gmail.com>
* fix(codex): restore cached tool call fields
* refactor(codex): merge duplicate enrich loops in chat history
enrich_call_item_from_cache copied the fill-if-empty loop for
reasoning_content/reasoning. The two loops are identical and key
order is irrelevant, so fold both key sets into a single loop.
Pure refactor, no behavior change; codex_chat_history tests pass.
---------
Co-authored-by: Jason <farion1231@gmail.com>
DeepSeek's Anthropic-compatible endpoint rejects requests where
thinking.type=disabled coexists with effort parameters, returning
HTTP 400. This breaks Claude Code 2.1.166+ sub-agents (Workflow/Dynamic
Workflow), which hardcode thinking:disabled.
Rather than overriding thinking:disabled, remove the conflicting effort
parameters (output_config.effort / reasoning_effort) to respect Claude
Code's intent — sub-agents don't need to display reasoning.
Fixes: https://github.com/deepseek-ai/DeepSeek-V3/issues/1397
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New zh/en/ja guide for the unified Codex session-history toggle: what opt-in migration (on enable) and ledger-based restore (on disable) actually do, why session data is never truly deleted (tag-only rewrite + automatic backups), and how to verify files on disk versus just being filed under another provider drawer. Includes a symptom reference table for the common "my sessions are gone" misunderstanding and on-disk verification commands for macOS/Linux/Windows.
Link the guide as the lead item in the "Usage Guides" section of the v3.16.3 release notes (zh/en/ja).
- Replace activity/agentplan links with the new codingplan invite URL
(ac=MMAP8JTTCAQ2, rc=6J6FV5N2) across all 6 app presets
- Refresh partnerPromotion copy in all 4 locales: two-month 75% off plus
invite code 6J6FV5N2; correct product name from Agent Plan to Coding Plan
- README: swap the CN-mainland redirect link in EN/DE, refresh the ZH promo
copy, and add the redirect link to the JA BytePlus entry
Remove the gold star badge and the partner promotion banner for MiniMax by
deleting the isPartner flag from all 12 MiniMax presets (cn + en) across
claude, claude-desktop, codex, opencode, openclaw, and hermes.
Both the preset-selector star (gated on isPartner) and the API-key promotion
banner (gated on isPartner && partnerPromotionKey) disappear as a result.
The partnerPromotionKey and the minimax_cn/minimax_en i18n copy are kept
dormant so the partnership can be re-enabled with a single line if needed.
MiniMax stays as a regular cn_official provider, keeping its icon and theme.
Codex ships as an npm platform-dispatch package (JS launcher @openai/codex + per-platform binary optional deps like @openai/codex-darwin-arm64). The upgrade chain ran `<bin>/codex update || <bin>/npm i -g @openai/codex@latest`, which can leave codex throwing "Missing optional dependency @openai/codex-darwin-arm64":
- `codex update` on an npm install is a bare `npm install -g @openai/codex` that prints success and exits 0 even when the platform binary fails to land, short-circuiting the `||` npm fallback.
- The npm fallback is a no-op when version==latest and only targets the main package, so it can never re-land the missing platform binary.
Fixes: (1) remove codex from prefers_official_update (Posix+Windows) so npm-managed codex no longer runs the false-success `codex update`; (2) add a runnable=false gate in installs_anchored_command emitting an uninstall+install self-heal — the only repair that re-lands the platform binary; (3) narrow by source/real to npm-managed sources (nvm/fnm/mise/homebrew, non-brew-formula) so broken brew-formula/volta/bun installs fall back to their own anchored commands instead of being mis-repaired with npm. Reuses the existing enumerate runnable signal; no new FS probing.
Settings uses Radix tabs, which unmount inactive tab content. Every time
the About tab was reopened, AboutSection remounted and its mount effect
re-ran all six tool version probes (a `--version` subprocess plus an
npm/github/pypi request each) -- wasteful, since versions rarely change
within a session and a manual Refresh already exists.
Add a module-scoped cache (lives for the app session, survives
unmount/remount) with a 10-minute TTL:
- on remount within the TTL the cached results are reused, skipping all
probes; state is lazily initialized from the cache so the first paint
shows the values with no loading flash
- a stale cache shows the old values immediately and revalidates per
tool in the background (stale-while-revalidate)
- the Refresh button forces a re-probe; single-tool refreshes (shell
change / post-update) update cached data without resetting the TTL
- cold-cache entries start at at=0 (a "not yet fully loaded" sentinel)
so a partial cache left by a mid-probe tab switch is treated as stale
and re-fetched rather than served as if complete; the real timestamp
is only stamped once a full load finishes
The app's own version is cached too, purely to avoid a loading flash on
remount.
getVersion() is a local, millisecond call but was awaited together with
loadAllToolVersions() in a single Promise.all, so setVersion and
setIsLoadingVersion only fired after all six tool checks finished. The
version badge under the app icon therefore waited for the whole batch.
Split the mount effect into two independent chains: loadAppVersion sets
the app version (and clears its loading flag) the moment getVersion()
resolves, while loadAllToolVersions runs its own progressive fan-out.
The two no longer block each other.
Fan out the six tool version probes concurrently instead of awaiting
one sequential batch, so each tool card updates the moment its own
check finishes rather than waiting for the slowest one.
- loadAllToolVersions now calls refreshToolVersions per tool via
Promise.all, reusing the existing per-name merge and per-tool
loading flags
- card loading derives from per-tool state
(loadingTools[t] || (isLoadingTools && !resolved)) so a resolved
card leaves loading independently while the batch is still in flight
- WSL shell/flag selects reuse the per-card loading flag, matching the
install/update buttons' early-enable behavior
This also drops total wall time from the sum of six sequential probes
to the slowest single probe. get_single_tool_version_impl is already
isolated and read-only (shared HTTP client, spawn_blocking subprocess,
no shared state), so concurrent probes are safe.
- useUsageQuery: retry once + keep-last-good — show the last successful
result for up to 10min when a query fails transiently (network/timeout/
HTTP 5xx), so a single blip no longer flips the card to red. Deterministic
failures (auth, empty key, unknown provider, 4xx) surface immediately and
clear the snapshot so a stale quota can't resurface after credentials change.
- bump native balance/coding-plan/subscription request timeouts 10s -> 15s
for slow cross-border endpoints.
- coding_plan: return explicit errors ("API key is empty" / "Unknown coding
plan provider") instead of a blank failure, mirroring balance.
- add unit tests for keep-last-good and transient/deterministic classification.
Display the Fable 5 Verified banner to the right of the app name and version block on the settings About page, marking this as a special build. Center the version badge under the app name so the two rows share a common axis.
The app-side asset lives at src/assets/fable5-verified.png (imported via the @ alias and bundled by Vite); the original source banner is kept under assets/partners/banners/ alongside the other partner banners.
Official providers (Claude/Codex/Gemini/Claude Desktop) use OAuth with an intentionally empty base_url and connect via the client's default endpoint, so cc-switch has no reliable reachability target. Probing a guessed endpoint either hits the wrong target or returns a meaningless green light. Hide the connectivity button for category === 'official'; reachability stays available for copilot/codex-oauth/third-party/custom providers, which is where the old real-request probe produced false negatives. Revert the official base_url fallback added earlier — resolve_base_url is back to extract-or-error.
The 1500ms degraded threshold was too strict; normal ~1s probe latencies showed as 'slow'. Restore the original 6000ms scale (default + config panel + per-provider range). Keep the reachability-appropriate 8s timeout / 1 retry.
The provider panel health check sent a real streaming model request, which many third-party providers block (401/403/WAF), causing false negatives while only stable official endpoints passed. Replace it with a lightweight reachability probe: GET the provider base_url and treat any HTTP response (200/4xx/5xx) as reachable; only DNS/connect/TLS/timeout count as failure. Latency is the probe's TTFB.
Backend (services/stream_check.rs): rewrite ~2200 -> ~350 lines, dropping real-request building, format conversion, auth and API-path resolution while keeping per-app base_url extraction. Defaults: 8s timeout, 1 retry, 1500ms degraded threshold.
Failover invariant: the reachability check must never reset the circuit breaker (reachable != usable; a 403 host is reachable but broken for real traffic). Remove the resetCircuitBreaker call from useStreamCheck; failover failure detection stays driven solely by real proxy traffic (forwarder/circuit_breaker untouched). useResetCircuitBreaker is kept dormant for a future manual-recovery entry.
Open the check to all providers: drop the official/copilot/codex-oauth/third-party gating and the 'sends a real request' confirm dialog. For official providers whose base_url is intentionally empty, fall back to the endpoint the client actually uses (Claude -> api.anthropic.com, Codex -> chatgpt.com/backend-api/codex, Gemini -> generativelanguage). Non-official providers with a missing base_url still error to avoid a false green light. Claude Desktop Official is native 1P mode (talks to claude.ai, cc-switch not in the request path, no reliable endpoint) so its button stays hidden.
Slim StreamCheckConfig and per-provider testConfig to timeout/threshold/retries (drop test model + prompt); sync zh/en/ja/zh-TW. Retain the now-unused anthropic_to_openai/anthropic_to_gemini transform utilities and their test suites.
* 调整预设供应商按钮外观与搜索框位置
1. 调整预设供应商按钮外观,显示默认图标,大小统一;
2. 调整预设供应商搜索框位置。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(provider): 新增预设按钮外观与 inline 搜索的单元测试
覆盖:
1. 所有预设按钮固定 200px 宽度,视觉对齐一致
2. preset.icon 存在时按钮内渲染 ProviderIcon
3. preset 无 icon 且无 theme.icon 时渲染占位元素保持文字对齐
4. 点击放大镜 inline 切换搜索输入框可见性,ESC 收起并清空
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(provider-preset): responsive grid layout and search polish
- Replace fixed-width preset buttons with a responsive CSS grid (auto-fill, 150px min column)
- Add a leading placeholder to the custom button so its label aligns with iconed presets
- Close the inline search box on outside click, restoring the old Popover behavior
- Span the empty-state hint across the full grid row
- Update component tests for the new layout and behaviors
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Jason <farion1231@gmail.com>
The Kimi For Coding preset was removed (74104946) because the coding
endpoint (api.kimi.com/coding/v1) enforces a User-Agent whitelist that
rejects Codex's default codex-cli UA with 403. The provider-level custom
User-Agent feature now lets users override the UA (to claude-cli/*) under
proxy takeover, so the preset can be restored.
- Re-add the Codex Kimi For Coding preset (openai_chat, kimi-for-coding,
256K context) in the same position it was removed from.
- Enable thinking mode by default via codexChatReasoning (supportsThinking
true, thinkingParam "thinking"), mirroring the general Kimi preset since
both target the same Moonshot model family. The proxy injects
thinking:{type:enabled} when Codex requests reasoning.
- Restore the trilingual user-manual row to "Kimi / Kimi For Coding".
Note: this preset requires proxy takeover and a whitelisted custom
User-Agent to work; the default codex-cli UA still gets 403.