Skip to content

Commit bedd94f

Browse files
committed
fix: align codex models and session ui
1 parent d8eb67e commit bedd94f

11 files changed

Lines changed: 293 additions & 188 deletions

lib/cli-models-utils.js

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,44 +48,105 @@ const ANTHROPIC_CLAUDE_MODELS = Object.freeze([
4848
'claude-3-haiku'
4949
]);
5050

51+
const DEEPSEEK_CLAUDE_COMPAT_MODELS = Object.freeze([
52+
'DeepSeek-V3.2',
53+
'DeepSeek-V3',
54+
'DeepSeek-R1',
55+
'deepseek-chat'
56+
]);
57+
58+
const QWEN_CLAUDE_COMPAT_MODELS = Object.freeze([
59+
'qwen3-coder',
60+
'qwen-max',
61+
'qwen-plus',
62+
'qwen-turbo'
63+
]);
64+
65+
const MODELSCOPE_CLAUDE_COMPAT_MODELS = Object.freeze([
66+
'ZhipuAI/GLM-5'
67+
]);
68+
5169
function normalizeModelCatalogId(value) {
5270
return typeof value === 'string' ? value.trim().toLowerCase() : '';
5371
}
5472

55-
function isBigModelClaudeCompatibleBaseUrl(baseUrl) {
73+
function hasPathSegment(baseUrl, segment) {
5674
const normalized = normalizeBaseUrl(baseUrl);
5775
if (!normalized) return false;
5876
try {
5977
const parsed = new URL(normalized);
60-
const host = String(parsed.hostname || '').toLowerCase();
6178
const pathname = String(parsed.pathname || '').toLowerCase();
62-
const isBigModelHost = host === 'bigmodel.cn' || host.endsWith('.bigmodel.cn');
63-
const hasAnthropicSegment = /(^|\/)anthropic(\/|$)/.test(pathname);
64-
return isBigModelHost && hasAnthropicSegment;
79+
const escaped = String(segment || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&').toLowerCase();
80+
return new RegExp(`(^|/)${escaped}(/|$)`).test(pathname);
6581
} catch (_) {
6682
return false;
6783
}
6884
}
6985

70-
function isAnthropicBaseUrl(baseUrl) {
86+
function getBaseUrlHost(baseUrl) {
7187
const normalized = normalizeBaseUrl(baseUrl);
72-
if (!normalized) return false;
88+
if (!normalized) return '';
7389
try {
7490
const parsed = new URL(normalized);
75-
const host = String(parsed.hostname || '').toLowerCase();
76-
return host === 'api.anthropic.com' || host.endsWith('.anthropic.com');
91+
return String(parsed.hostname || '').toLowerCase();
7792
} catch (_) {
78-
return false;
93+
return '';
7994
}
8095
}
8196

97+
function isHostOrSubdomain(host, domain) {
98+
return host === domain || host.endsWith(`.${domain}`);
99+
}
100+
101+
function isBigModelClaudeCompatibleBaseUrl(baseUrl) {
102+
const host = getBaseUrlHost(baseUrl);
103+
return isHostOrSubdomain(host, 'bigmodel.cn') && hasPathSegment(baseUrl, 'anthropic');
104+
}
105+
106+
function isAnthropicBaseUrl(baseUrl) {
107+
const host = getBaseUrlHost(baseUrl);
108+
return isHostOrSubdomain(host, 'anthropic.com');
109+
}
110+
111+
function isDeepSeekClaudeCompatibleBaseUrl(baseUrl) {
112+
const host = getBaseUrlHost(baseUrl);
113+
return isHostOrSubdomain(host, 'deepseek.com') && hasPathSegment(baseUrl, 'anthropic');
114+
}
115+
116+
function isQwenClaudeCompatibleBaseUrl(baseUrl) {
117+
const host = getBaseUrlHost(baseUrl);
118+
return isHostOrSubdomain(host, 'dashscope.aliyuncs.com') && hasPathSegment(baseUrl, 'anthropic');
119+
}
120+
121+
function isZaiClaudeCompatibleBaseUrl(baseUrl) {
122+
const host = getBaseUrlHost(baseUrl);
123+
return isHostOrSubdomain(host, 'z.ai') && hasPathSegment(baseUrl, 'anthropic');
124+
}
125+
126+
function isModelScopeBaseUrl(baseUrl) {
127+
const host = getBaseUrlHost(baseUrl);
128+
return isHostOrSubdomain(host, 'modelscope.cn');
129+
}
130+
82131
function getSupplementalModelsForBaseUrl(baseUrl) {
83132
if (isBigModelClaudeCompatibleBaseUrl(baseUrl)) {
84133
return [...BIGMODEL_CLAUDE_COMPAT_MODELS];
85134
}
86135
if (isAnthropicBaseUrl(baseUrl)) {
87136
return [...ANTHROPIC_CLAUDE_MODELS];
88137
}
138+
if (isDeepSeekClaudeCompatibleBaseUrl(baseUrl)) {
139+
return [...DEEPSEEK_CLAUDE_COMPAT_MODELS];
140+
}
141+
if (isQwenClaudeCompatibleBaseUrl(baseUrl)) {
142+
return [...QWEN_CLAUDE_COMPAT_MODELS];
143+
}
144+
if (isZaiClaudeCompatibleBaseUrl(baseUrl)) {
145+
return [...BIGMODEL_CLAUDE_COMPAT_MODELS];
146+
}
147+
if (isModelScopeBaseUrl(baseUrl)) {
148+
return [...MODELSCOPE_CLAUDE_COMPAT_MODELS];
149+
}
89150
return [];
90151
}
91152

tests/unit/cli-models-utils.test.mjs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,25 @@ test('getSupplementalModelsForBaseUrl returns Anthropic Claude models for offici
3232
assert(!models.includes('glm-5.1'));
3333
});
3434

35+
test('getSupplementalModelsForBaseUrl returns provider-specific Claude-compatible Codex catalogs', () => {
36+
const deepseekModels = getSupplementalModelsForBaseUrl('https://api.deepseek.com/anthropic');
37+
const qwenModels = getSupplementalModelsForBaseUrl('https://coding.dashscope.aliyuncs.com/apps/anthropic');
38+
const zaiModels = getSupplementalModelsForBaseUrl('https://api.z.ai/api/anthropic');
39+
const modelscopeModels = getSupplementalModelsForBaseUrl('https://api-inference.modelscope.cn');
40+
41+
assert(deepseekModels.includes('DeepSeek-V3.2'));
42+
assert(!deepseekModels.includes('qwen3-coder'));
43+
assert(qwenModels.includes('qwen3-coder'));
44+
assert(!qwenModels.includes('DeepSeek-V3.2'));
45+
assert(zaiModels.includes('glm-5'));
46+
assert(modelscopeModels.includes('ZhipuAI/GLM-5'));
47+
});
48+
3549
test('getSupplementalModelsForBaseUrl does not match unrelated bigmodel hosts or paths', () => {
3650
assert.deepStrictEqual(getSupplementalModelsForBaseUrl('https://notbigmodel.cn/api/anthropic'), []);
3751
assert.deepStrictEqual(getSupplementalModelsForBaseUrl('https://open.bigmodel.cn/api/anthropicx'), []);
52+
assert.deepStrictEqual(getSupplementalModelsForBaseUrl('https://api.deepseek.com/v1'), []);
53+
assert.deepStrictEqual(getSupplementalModelsForBaseUrl('https://coding.dashscope.aliyuncs.com/apps/openai'), []);
3854
});
3955

4056
test('mergeModelCatalog keeps remote order and appends missing Claude endpoint extras once', () => {

tests/unit/compact-layout-ui.test.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ test('styles keep desktop layout wide and session history readable on large scre
6262
assert.match(styles, /\.session-layout\s*\{[\s\S]*grid-template-columns:\s*minmax\(260px,\s*360px\)\s*minmax\(0,\s*1fr\);/);
6363
assert.match(styles, /\.session-preview-scroll\s*\{[\s\S]*padding-right:\s*52px;/);
6464
assert.match(styles, /\.session-timeline\s*\{[\s\S]*right:\s*4px;[\s\S]*width:\s*44px;/);
65-
assert.match(styles, /\.session-item\s*\{[\s\S]*min-height:\s*80px;/);
65+
assert.match(styles, /\.session-item\s*\{[\s\S]*min-height:\s*108px;[\s\S]*contain-intrinsic-size:\s*108px;/);
66+
assert.match(styles, /\.session-item-cwd\s*\{[\s\S]*flex:\s*1 0 100%;[\s\S]*white-space:\s*normal;[\s\S]*overflow:\s*visible;[\s\S]*overflow-wrap:\s*anywhere;/);
67+
assert.doesNotMatch(styles, /@media \(max-width: 540px\)\s*\{[\s\S]*\.session-item\s*\{[\s\S]*height:\s*75px;/);
6668

6769
const html = readBundledWebUiHtml();
6870
assert.match(html, /class="brand-logo"\s+src="\/res\/logo-pack\.webp"/);

web-ui/styles/base-theme.css

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,36 @@
44
设计系统 - Design Tokens
55
============================================ */
66
:root {
7-
/* 色彩系统:中性灰(更贴近 craft/shadcn 观感)+ 品牌强调 */
8-
--color-brand: #C77462;
9-
--color-brand-dark: #B45E4E;
10-
--color-brand-light: rgba(199, 116, 98, 0.12);
11-
--color-brand-subtle: rgba(199, 116, 98, 0.18);
12-
13-
--color-bg: #FAFAFA;
14-
--color-surface: #FFFFFF;
15-
--color-surface-alt: #F4F4F5;
7+
/* 色彩系统:低饱和暖色 + 桌面助理式柔和层级 */
8+
--color-brand: #C87963;
9+
--color-brand-dark: #A95845;
10+
--color-brand-light: rgba(200, 121, 99, 0.13);
11+
--color-brand-subtle: rgba(200, 121, 99, 0.2);
12+
13+
--color-bg: #F7F0E9;
14+
--color-surface: #FFFDFC;
15+
--color-surface-alt: #F7EFE8;
1616
--color-surface-elevated: #FFFFFF;
17-
--color-surface-tint: rgba(255, 255, 255, 0.92);
18-
--color-text-primary: #18181B;
19-
--color-text-secondary: #3F3F46;
20-
--color-text-tertiary: #71717A;
21-
--color-text-muted: #A1A1AA;
22-
--color-border: #E4E4E7;
23-
--color-border-soft: rgba(24, 24, 27, 0.12);
24-
--color-border-strong: rgba(24, 24, 27, 0.24);
17+
--color-surface-tint: rgba(255, 253, 250, 0.86);
18+
--color-text-primary: #241F1C;
19+
--color-text-secondary: #5A504A;
20+
--color-text-tertiary: #82746A;
21+
--color-text-muted: #A99B91;
22+
--color-border: rgba(137, 111, 94, 0.18);
23+
--color-border-soft: rgba(137, 111, 94, 0.13);
24+
--color-border-strong: rgba(137, 111, 94, 0.34);
2525

2626
--color-success: #4B8B6A;
2727
--color-error: #C44536;
2828

2929
--bg-warm-gradient:
30-
linear-gradient(180deg, #FAFAFA 0%, #FAFAFA 100%);
30+
radial-gradient(circle at 14% 8%, rgba(255, 219, 196, 0.5) 0%, rgba(255, 219, 196, 0) 32%),
31+
radial-gradient(circle at 88% 0%, rgba(252, 239, 207, 0.58) 0%, rgba(252, 239, 207, 0) 30%),
32+
linear-gradient(135deg, #FFF8F1 0%, #F7F0E9 46%, #F1E8DF 100%);
3133

32-
--color-bg-topbar-strong: rgba(250, 250, 250, 0.96);
33-
--color-bg-topbar-soft: rgba(250, 250, 250, 0.9);
34-
--color-bg-topbar-clear: rgba(250, 250, 250, 0);
34+
--color-bg-topbar-strong: rgba(255, 248, 241, 0.96);
35+
--color-bg-topbar-soft: rgba(255, 248, 241, 0.86);
36+
--color-bg-topbar-clear: rgba(255, 248, 241, 0);
3537

3638
/* 字体系统 */
3739
--font-family-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
@@ -64,24 +66,24 @@
6466
--spacing-xl: 64px;
6567

6668
/* 圆角系统 */
67-
--radius-sm: 8px;
68-
--radius-md: 10px;
69-
--radius-lg: 12px;
70-
--radius-xl: 16px;
69+
--radius-sm: 10px;
70+
--radius-md: 14px;
71+
--radius-lg: 18px;
72+
--radius-xl: 24px;
7173
--radius-full: 50px;
7274

73-
/* 阴影系统 - 更偏中性与克制 */
74-
--shadow-subtle: 0 1px 2px rgba(24, 24, 27, 0.04);
75-
--shadow-card: 0 1px 3px rgba(24, 24, 27, 0.06);
76-
--shadow-card-hover: 0 8px 26px rgba(24, 24, 27, 0.12);
77-
--shadow-float: 0 14px 36px rgba(24, 24, 27, 0.16);
78-
--shadow-raised: 0 8px 18px rgba(24, 24, 27, 0.12);
75+
/* 阴影系统 - 柔和桌面浮层 */
76+
--shadow-subtle: 0 1px 2px rgba(60, 47, 38, 0.05);
77+
--shadow-card: 0 12px 30px rgba(92, 68, 52, 0.08);
78+
--shadow-card-hover: 0 18px 44px rgba(92, 68, 52, 0.14);
79+
--shadow-float: 0 22px 56px rgba(70, 51, 39, 0.18);
80+
--shadow-raised: 0 14px 30px rgba(92, 68, 52, 0.14);
7981
--shadow-modal:
80-
0 10px 30px rgba(24, 24, 27, 0.14),
81-
0 28px 72px rgba(24, 24, 27, 0.12);
82+
0 16px 42px rgba(70, 51, 39, 0.16),
83+
0 34px 84px rgba(70, 51, 39, 0.16);
8284
--shadow-input-focus:
8385
0 0 0 3px var(--color-brand-light),
84-
0 1px 2px rgba(24, 24, 27, 0.06);
86+
0 1px 2px rgba(60, 47, 38, 0.06);
8587

8688
/* 动画 - 更细腻的曲线 */
8789
--transition-instant: 100ms;
@@ -266,3 +268,14 @@ body {
266268
position: relative;
267269
overflow-x: hidden;
268270
}
271+
272+
body::before {
273+
content: "";
274+
position: fixed;
275+
inset: 0;
276+
pointer-events: none;
277+
background:
278+
radial-gradient(circle at 18% 16%, rgba(255, 255, 255, 0.62), rgba(255, 255, 255, 0) 26%),
279+
radial-gradient(circle at 86% 18%, rgba(255, 232, 206, 0.36), rgba(255, 232, 206, 0) 30%);
280+
z-index: 0;
281+
}

0 commit comments

Comments
 (0)