From e05e3f5ec2b31a4d3f7f3d3ac1ec7de1b896ce84 Mon Sep 17 00:00:00 2001 From: qer Date: Wed, 17 Jun 2026 23:34:18 +0800 Subject: [PATCH 1/2] feat(web): group default model dropdown by provider in settings --- .../group-settings-model-by-provider.md | 5 ++ .../src/components/SettingsDialog.vue | 58 ++++++++++++++----- apps/kimi-web/test/settings-dialog.test.ts | 18 ++++++ 3 files changed, 68 insertions(+), 13 deletions(-) create mode 100644 .changeset/group-settings-model-by-provider.md diff --git a/.changeset/group-settings-model-by-provider.md b/.changeset/group-settings-model-by-provider.md new file mode 100644 index 0000000000..569cdc57e0 --- /dev/null +++ b/.changeset/group-settings-model-by-provider.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Group the default model dropdown in web settings by provider. diff --git a/apps/kimi-web/src/components/SettingsDialog.vue b/apps/kimi-web/src/components/SettingsDialog.vue index c5c59f4db5..6ca9faab12 100644 --- a/apps/kimi-web/src/components/SettingsDialog.vue +++ b/apps/kimi-web/src/components/SettingsDialog.vue @@ -76,19 +76,42 @@ function exportLog(): void { downloadTraceLog(); } -type ModelOption = { id: string; label: string }; +type ModelOption = { id: string; label: string; provider: string }; const modelOptions = computed(() => { - const byId = new Map(); + const byId = new Map(); for (const model of props.models ?? []) { - byId.set(model.id, model.displayName ?? model.model ?? model.id); + byId.set(model.id, { + id: model.id, + label: model.displayName ?? model.model ?? model.id, + provider: model.provider, + }); } for (const [id, raw] of Object.entries(props.config?.models ?? {})) { if (byId.has(id)) continue; - byId.set(id, formatConfigModelLabel(id, raw)); + const provider = extractConfigModelProvider(raw); + byId.set(id, { + id, + label: formatConfigModelLabel(id, raw, provider), + provider: provider ?? id, + }); } - return Array.from(byId, ([id, label]) => ({ id, label })) - .sort((a, b) => a.label.localeCompare(b.label)); + return Array.from(byId.values()); +}); + +const modelGroups = computed>(() => { + const map = new Map(); + for (const option of modelOptions.value) { + const list = map.get(option.provider) ?? []; + list.push(option); + map.set(option.provider, list); + } + for (const list of map.values()) { + list.sort((a, b) => a.label.localeCompare(b.label)); + } + return Array.from(map.entries()) + .toSorted(([a], [b]) => a.localeCompare(b)) + .map(([provider, options]) => ({ provider, options })); }); const providerEntries = computed>(() => @@ -102,12 +125,19 @@ const defaultPermissionMode = computed(() => { return mode === 'auto' || mode === 'yolo' || mode === 'manual' ? mode : 'manual'; }); -function formatConfigModelLabel(id: string, raw: unknown): string { +function extractConfigModelProvider(raw: unknown): string | undefined { + if (!raw || typeof raw !== 'object') return undefined; + const source = raw as Record; + const provider = typeof source['provider'] === 'string' ? source['provider'] : undefined; + return provider; +} + +function formatConfigModelLabel(id: string, raw: unknown, provider?: string): string { if (!raw || typeof raw !== 'object') return id; const source = raw as Record; const model = typeof source['model'] === 'string' ? source['model'] : undefined; - const provider = typeof source['provider'] === 'string' ? source['provider'] : undefined; - if (model && provider) return `${id} (${provider}/${model})`; + const resolvedProvider = provider ?? extractConfigModelProvider(raw); + if (model && resolvedProvider) return `${id} (${resolvedProvider}/${model})`; if (model) return `${id} (${model})`; return id; } @@ -271,7 +301,7 @@ function setTab(tab: SettingsTab): void { {{ t('settings.defaultModelHint') }} {{ config.defaultModel ?? t('settings.noDefaultModel') }} diff --git a/apps/kimi-web/test/settings-dialog.test.ts b/apps/kimi-web/test/settings-dialog.test.ts index 4e1f4fa470..e2499b3442 100644 --- a/apps/kimi-web/test/settings-dialog.test.ts +++ b/apps/kimi-web/test/settings-dialog.test.ts @@ -162,6 +162,24 @@ describe('SettingsDialog config controls', () => { await planRow!.find('button.switch').trigger('click'); expect(wrapper.emitted('updateConfig')?.[2]?.[0]).toEqual({ defaultPlanMode: true }); }); + + it('groups default model options by provider', async () => { + const wrapper = mountDialog(); + + const agentTab = wrapper.findAll('.tab').find((button) => button.text() === 'Agent'); + await agentTab!.trigger('click'); + + const groups = wrapper.findAll('optgroup'); + expect(groups.length).toBe(2); + expect(groups[0]!.attributes('label')).toBe('kimi'); + expect(groups[1]!.attributes('label')).toBe('openai'); + + const kimiOptions = groups[0]!.findAll('option'); + expect(kimiOptions.some((o) => o.attributes('value') === 'kimi/k2')).toBe(true); + + const openaiOptions = groups[1]!.findAll('option'); + expect(openaiOptions.some((o) => o.attributes('value') === 'openai/gpt-5')).toBe(true); + }); }); describe('SettingsDialog dialog focus', () => { From a9fdf9b7227d540582c9342982c039c243111e3d Mon Sep 17 00:00:00 2001 From: qer Date: Wed, 17 Jun 2026 23:43:21 +0800 Subject: [PATCH 2/2] fix(web): prevent login dialog closing on backdrop click --- .changeset/disable-login-backdrop-close.md | 5 +++++ apps/kimi-web/src/components/LoginDialog.vue | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/disable-login-backdrop-close.md diff --git a/.changeset/disable-login-backdrop-close.md b/.changeset/disable-login-backdrop-close.md new file mode 100644 index 0000000000..599e5f666b --- /dev/null +++ b/.changeset/disable-login-backdrop-close.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Prevent the web login dialog from closing when clicking the backdrop. diff --git a/apps/kimi-web/src/components/LoginDialog.vue b/apps/kimi-web/src/components/LoginDialog.vue index 5d35a27d18..71a50bee29 100644 --- a/apps/kimi-web/src/components/LoginDialog.vue +++ b/apps/kimi-web/src/components/LoginDialog.vue @@ -197,7 +197,7 @@ function formatSeconds(s: number): string {